Changeset ba776282 in rtems


Ignore:
Timestamp:
Aug 12, 2016, 7:25:10 PM (3 years ago)
Author:
Gedare Bloom <gedare@…>
Branches:
master
Children:
3a46b72
Parents:
2b442a8e
git-author:
Gedare Bloom <gedare@…> (08/12/16 19:25:10)
git-committer:
Gedare Bloom <gedare@…> (01/13/17 16:17:30)
Message:

posix: shared memory support

Add POSIX shared memory manager (Shm). Includes a hook-based
approach for the backing memory storage that defaults to the
Workspace, and a test is provided using the heap. A test is
also provided for the basic use of mmap'ing a shared memory
object. This test currently fails at the mmap stage due to
no support for mmap.

Files:
13 added
12 edited

Legend:

Unmodified
Added
Removed
  • cpukit/posix/Makefile.am

    r2b442a8e rba776282  
    4242include_rtems_posix_HEADERS += include/rtems/posix/pthreadimpl.h
    4343include_rtems_posix_HEADERS += include/rtems/posix/ptimer.h
     44include_rtems_posix_HEADERS += include/rtems/posix/shm.h
     45include_rtems_posix_HEADERS += include/rtems/posix/shmimpl.h
    4446include_rtems_posix_HEADERS += include/rtems/posix/semaphore.h
    4547include_rtems_posix_HEADERS += include/rtems/posix/semaphoreimpl.h
     
    99101libposix_a_SOURCES += src/munmap.c
    100102libposix_a_SOURCES += src/posix_madvise.c
     103libposix_a_SOURCES += src/shm.c
     104libposix_a_SOURCES += src/shmheap.c
    101105libposix_a_SOURCES += src/shmopen.c
    102106libposix_a_SOURCES += src/shmunlink.c
     107libposix_a_SOURCES += src/shmwkspace.c
    103108
    104109## MESSAGE_QUEUE_C_FILES
  • cpukit/posix/include/rtems/posix/config.h

    r2b442a8e rba776282  
    112112
    113113  /**
     114   * Maximum configured number of POSIX Shared memory objects.
     115   */
     116  uint32_t                            maximum_shms;
     117
     118  /**
    114119   * This field contains the number of POSIX API Initialization
    115120   * threads listed in @a User_initialization_thread_table.
  • cpukit/posix/preinstall.am

    r2b442a8e rba776282  
    109109PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/posix/ptimer.h
    110110
     111$(PROJECT_INCLUDE)/rtems/posix/shm.h: include/rtems/posix/shm.h $(PROJECT_INCLUDE)/rtems/posix/$(dirstamp)
     112        $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/posix/shm.h
     113PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/posix/shm.h
     114
     115$(PROJECT_INCLUDE)/rtems/posix/shmimpl.h: include/rtems/posix/shmimpl.h $(PROJECT_INCLUDE)/rtems/posix/$(dirstamp)
     116        $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/posix/shmimpl.h
     117PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/posix/shmimpl.h
     118
    111119$(PROJECT_INCLUDE)/rtems/posix/semaphore.h: include/rtems/posix/semaphore.h $(PROJECT_INCLUDE)/rtems/posix/$(dirstamp)
    112120        $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/posix/semaphore.h
  • cpukit/posix/src/shmopen.c

    r2b442a8e rba776282  
    1717#include <sys/mman.h>
    1818#include <errno.h>
     19#include <unistd.h>
     20#include <fcntl.h>
     21#include <sys/stat.h>
     22
     23#include <rtems/libio_.h>
    1924#include <rtems/seterr.h>
    2025
     26#include <rtems/posix/shmimpl.h>
     27#include <rtems/score/wkspace.h>
     28
     29static const rtems_filesystem_file_handlers_r shm_handlers;
     30
     31static int shm_fstat(
     32  const rtems_filesystem_location_info_t *loc,
     33  struct stat *buf
     34)
     35{
     36  POSIX_Shm_Control *shm = loc_to_shm( loc );
     37
     38  if ( shm == NULL )
     39    rtems_set_errno_and_return_minus_one( EIO );
     40
     41  /* mandatory for shm objects */
     42  buf->st_uid = shm->uid;
     43  buf->st_gid = shm->gid;
     44  buf->st_size = shm->shm_object.size;
     45  buf->st_mode = shm->mode;
     46
     47  /* optional for shm objects */
     48  buf->st_atime = shm->atime;
     49  buf->st_mtime = shm->mtime;
     50  buf->st_ctime = shm->ctime;
     51
     52  return 0;
     53}
     54
     55/* read() is unspecified for shared memory objects */
     56static ssize_t shm_read( rtems_libio_t *iop, void *buffer, size_t count )
     57{
     58  Thread_queue_Context queue_context;
     59  ssize_t bytes_read;
     60  POSIX_Shm_Control *shm = iop_to_shm( iop );
     61
     62  _Thread_queue_Context_initialize( &queue_context );
     63  _POSIX_Shm_Acquire( shm, &queue_context );
     64  bytes_read = (*shm->shm_object.ops->object_read)(
     65      &shm->shm_object,
     66      buffer,
     67      count
     68  );
     69  _POSIX_Shm_Release( shm, &queue_context );
     70
     71  return bytes_read;
     72}
     73
     74static int shm_ftruncate( rtems_libio_t *iop, off_t length )
     75{
     76  Thread_queue_Context queue_context;
     77  int err;
     78  POSIX_Shm_Control *shm = iop_to_shm( iop );
     79
     80  _Thread_queue_Context_initialize( &queue_context );
     81  _POSIX_Shm_Acquire( shm, &queue_context );
     82
     83  err = (*shm->shm_object.ops->object_resize)( &shm->shm_object, length );
     84
     85  if ( err != 0 ) {
     86    _POSIX_Shm_Release( shm, &queue_context );
     87    rtems_set_errno_and_return_minus_one( err );
     88  }
     89
     90  _POSIX_Shm_Update_mtime_ctime( shm );
     91
     92  _POSIX_Shm_Release( shm, &queue_context );
     93  return 0;
     94}
     95
     96static int shm_close( rtems_libio_t *iop )
     97{
     98  POSIX_Shm_Control *shm = iop_to_shm( iop );
     99  Objects_Control       *obj;
     100  ISR_lock_Context       lock_ctx;
     101  int err;
     102
     103  _Objects_Allocator_lock();
     104
     105  --shm->reference_count;
     106  if ( shm->reference_count == 0 ) {
     107    /* TODO: need to make sure this counts mmaps too! */
     108    if ( (*shm->shm_object.ops->object_delete)( &shm->shm_object ) != 0 ) {
     109      err = EIO;
     110    }
     111    /* check if the object has been unlinked yet. */
     112    obj = _Objects_Get( shm->Object.id, &lock_ctx, &_POSIX_Shm_Information );
     113    if ( obj == NULL ) {
     114      /* if it was unlinked, then it can be freed. */
     115      _POSIX_Shm_Free( shm );
     116    } else {
     117      /* it will be freed when it is unlinked. */
     118      _ISR_lock_ISR_enable( &lock_ctx );
     119    }
     120  }
     121  iop->pathinfo.node_access = NULL;
     122
     123  _Objects_Allocator_unlock();
     124
     125  if ( err != 0 ) {
     126    rtems_set_errno_and_return_minus_one( err );
     127  }
     128  return 0;
     129}
     130
     131static inline POSIX_Shm_Control *shm_allocate(
     132  const char *name_arg,
     133  size_t name_len,
     134  int oflag,
     135  mode_t mode,
     136  int *error
     137)
     138{
     139  POSIX_Shm_Control *shm;
     140  char *name;
     141  struct timeval tv;
     142
     143  /* Reject any name without a leading slash. */
     144  if ( name_arg[0] != '/' ) {
     145    *error = EINVAL;
     146    return NULL;
     147  }
     148
     149  /* Only create the object if requested. */
     150  if ( ( oflag & O_CREAT ) != O_CREAT ) {
     151    *error = ENOENT;
     152    return NULL;
     153  }
     154
     155  name = _Workspace_String_duplicate( name_arg, name_len );
     156  if ( name == NULL ) {
     157    *error = ENOSPC;
     158    return NULL;
     159  }
     160
     161  shm = _POSIX_Shm_Allocate_unprotected();
     162  if ( shm == NULL ) {
     163    _Workspace_Free( name );
     164    *error = ENFILE;
     165    return NULL;
     166  }
     167
     168  gettimeofday( &tv, 0 );
     169
     170  shm->reference_count = 1;
     171  shm->shm_object.handle = NULL;
     172  shm->shm_object.size = 0;
     173  shm->shm_object.ops = &_POSIX_Shm_Object_operations;
     174  shm->mode = mode & ~rtems_filesystem_umask;
     175  shm->uid = geteuid();
     176  shm->gid = getegid();
     177  shm->atime = (time_t) tv.tv_sec;
     178  shm->mtime = (time_t) tv.tv_sec;
     179  shm->ctime = (time_t) tv.tv_sec;
     180
     181  _Objects_Open_string( &_POSIX_Shm_Information, &shm->Object, name );
     182
     183  return shm;
     184}
     185
     186static inline bool shm_access_ok( POSIX_Shm_Control *shm, int oflag )
     187{
     188  int flags;
     189  if ( oflag & O_RDONLY ) {
     190    flags = RTEMS_FS_PERMS_READ;
     191  } else {
     192    flags = RTEMS_FS_PERMS_WRITE;
     193  }
     194  return rtems_filesystem_check_access( flags, shm->mode, shm->uid, shm->gid );
     195}
     196
     197static inline int shm_check_oflag( int oflag )
     198{
     199  if ( ( oflag & O_ACCMODE ) != O_RDONLY && ( oflag & O_ACCMODE ) != O_RDWR ) {
     200    rtems_set_errno_and_return_minus_one( EACCES );
     201  }
     202
     203  if ( ( oflag & ~( O_RDONLY | O_RDWR | O_CREAT | O_EXCL | O_TRUNC ) ) != 0 ) {
     204    rtems_set_errno_and_return_minus_one( EACCES );
     205  }
     206
     207  if ( ( oflag & O_TRUNC ) != 0 && ( oflag & O_ACCMODE ) != O_RDWR ) {
     208    rtems_set_errno_and_return_minus_one( EACCES );
     209  }
     210  return 0;
     211}
     212
    21213int shm_open( const char *name, int oflag, mode_t mode )
    22214{
    23   (void) name;
    24   (void) oflag;
    25   (void) mode;
    26 
    27   rtems_set_errno_and_return_minus_one( EINVAL );
    28 }
     215  int err = 0;
     216  int fd;
     217  rtems_libio_t *iop;
     218  POSIX_Shm_Control *shm;
     219  size_t len;
     220  Objects_Get_by_name_error obj_err;
     221
     222  if ( shm_check_oflag( oflag ) != 0 ) {
     223    return -1;
     224  }
     225
     226  iop = rtems_libio_allocate();
     227  if ( iop == NULL ) {
     228    rtems_set_errno_and_return_minus_one( EMFILE );
     229  }
     230
     231  _Objects_Allocator_lock();
     232  shm = _POSIX_Shm_Get_by_name( name, &len, &obj_err );
     233
     234  if ( shm == NULL ) {
     235    switch ( obj_err ) {
     236      case OBJECTS_GET_BY_NAME_INVALID_NAME:
     237        err = EINVAL;
     238        break;
     239
     240      case OBJECTS_GET_BY_NAME_NAME_TOO_LONG:
     241        err = ENAMETOOLONG;
     242        break;
     243
     244      case OBJECTS_GET_BY_NAME_NO_OBJECT:
     245      default:
     246        shm = shm_allocate(name, len, oflag, mode, &err);
     247        break;
     248    }
     249  } else { /* shm exists */
     250    if ( ( oflag & ( O_EXCL | O_CREAT ) ) == ( O_EXCL | O_CREAT ) ) {
     251      /* Request to create failed. */
     252      err = EEXIST;
     253    } else if ( !shm_access_ok( shm, oflag ) ) {
     254      err = EACCES;
     255    } else {
     256      ++shm->reference_count;
     257    }
     258  }
     259  _Objects_Allocator_unlock();
     260  if ( err != 0 ) {
     261    rtems_libio_free( iop );
     262    rtems_set_errno_and_return_minus_one( err );
     263  }
     264
     265  if ( oflag & O_TRUNC ) {
     266    err = shm_ftruncate( iop, 0 );
     267    (void) err; /* ignore truncate error */
     268  }
     269
     270  fd = rtems_libio_iop_to_descriptor( iop );
     271  iop->flags |= LIBIO_FLAGS_CLOSE_ON_EXEC;
     272  if ( oflag & O_RDONLY ) {
     273    iop->flags |= LIBIO_FLAGS_READ;
     274  } else {
     275    iop->flags |= LIBIO_FLAGS_READ_WRITE;
     276  }
     277  iop->data0 = fd;
     278  iop->data1 = shm;
     279  iop->pathinfo.node_access = shm;
     280  iop->pathinfo.handlers = &shm_handlers;
     281  iop->pathinfo.mt_entry = &rtems_filesystem_null_mt_entry;
     282  rtems_filesystem_location_add_to_mt_entry( &iop->pathinfo );
     283
     284  return fd;
     285}
     286
     287static const rtems_filesystem_file_handlers_r shm_handlers = {
     288  .open_h = rtems_filesystem_default_open,
     289  .close_h = shm_close,
     290  .read_h = shm_read,
     291  .write_h = rtems_filesystem_default_write,
     292  .ioctl_h = rtems_filesystem_default_ioctl,
     293  .lseek_h = rtems_filesystem_default_lseek,
     294  .fstat_h = shm_fstat,
     295  .ftruncate_h = shm_ftruncate,
     296  .fsync_h = rtems_filesystem_default_fsync_or_fdatasync,
     297  .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
     298  .fcntl_h = rtems_filesystem_default_fcntl,
     299  .kqfilter_h = rtems_filesystem_default_kqfilter,
     300  .poll_h = rtems_filesystem_default_poll,
     301  .readv_h = rtems_filesystem_default_readv,
     302  .writev_h = rtems_filesystem_default_writev
     303};
  • cpukit/posix/src/shmunlink.c

    r2b442a8e rba776282  
    1919#include <rtems/seterr.h>
    2020
     21#include <rtems/posix/shmimpl.h>
     22
    2123int shm_unlink( const char *name )
    2224{
    23   (void) name;
     25  Objects_Get_by_name_error obj_err;
     26  int err = 0;
     27  POSIX_Shm_Control *shm;
    2428
    25   rtems_set_errno_and_return_minus_one( ENOENT );
     29  shm = _POSIX_Shm_Get_by_name( name, 0, &obj_err );
     30  switch ( obj_err ) {
     31    case OBJECTS_GET_BY_NAME_INVALID_NAME:
     32      err = ENOENT;
     33      break;
     34
     35    case OBJECTS_GET_BY_NAME_NAME_TOO_LONG:
     36      err = ENAMETOOLONG;
     37      break;
     38
     39    case OBJECTS_GET_BY_NAME_NO_OBJECT:
     40    default:
     41      _Objects_Close( &_POSIX_Shm_Information, &shm->Object );
     42      if ( shm->reference_count == 0 ) {
     43        /* TODO: need to make sure this counts mmaps too! */
     44        /* only remove the shm object if no references exist to it. */
     45        _POSIX_Shm_Free( shm );
     46      }
     47      break;
     48  }
     49  if ( err != 0 )
     50    rtems_set_errno_and_return_minus_one( err );
     51  return 0;
    2652}
  • cpukit/rtems/src/rtemsobjectgetapiclassname.c

    r2b442a8e rba776282  
    5656  { "Barrier",                 OBJECTS_POSIX_BARRIERS, 0},
    5757  { "RWLock",                  OBJECTS_POSIX_RWLOCKS, 0},
     58  { "Shared Memory",           OBJECTS_POSIX_SHMS, 0},
    5859  { NULL,                      0, 0}
    5960};
  • cpukit/sapi/include/confdefs.h

    r2b442a8e rba776282  
    20692069        rtems_resource_unlimited(CONFIGURE_UNLIMITED_ALLOCATION_SIZE)
    20702070    #endif
     2071    #if !defined(CONFIGURE_MAXIMUM_POSIX_SHMS)
     2072      #define CONFIGURE_MAXIMUM_POSIX_SHMS \
     2073        rtems_resource_unlimited(CONFIGURE_UNLIMITED_ALLOCATION_SIZE)
     2074    #endif
    20712075  #endif /* RTEMS_POSIX_API */
    20722076#endif /* CONFIGURE_UNLIMITED_OBJECTS */
     
    24652469  #include <rtems/posix/rwlock.h>
    24662470  #include <rtems/posix/semaphore.h>
     2471  #include <rtems/posix/shm.h>
    24672472  #include <rtems/posix/threadsup.h>
    24682473  #include <rtems/posix/timer.h>
     
    26262631  #define CONFIGURE_MEMORY_FOR_POSIX_RWLOCKS(_rwlocks) \
    26272632    _Configure_Object_RAM(_rwlocks, sizeof(POSIX_RWLock_Control) )
     2633
     2634  /**
     2635   * Configure the maximum number of POSIX shared memory objects.
     2636   */
     2637  #if !defined(CONFIGURE_MAXIMUM_POSIX_SHMS)
     2638    #define CONFIGURE_MAXIMUM_POSIX_SHMS 0
     2639  #else
     2640    #ifdef CONFIGURE_INIT
     2641      #if !defined(CONFIGURE_HAS_OWN_POSIX_SHM_OBJECT_OPERATIONS)
     2642        const POSIX_Shm_Object_operations _POSIX_Shm_Object_operations = {
     2643          _POSIX_Shm_Object_create_from_workspace,
     2644          _POSIX_Shm_Object_resize_from_workspace,
     2645          _POSIX_Shm_Object_delete_from_workspace,
     2646          _POSIX_Shm_Object_read_from_workspace
     2647        };
     2648      #endif
     2649    #endif
     2650  #endif
     2651
     2652  /**
     2653   * This macro is calculated to specify the memory required for
     2654   * POSIX API shared memory.
     2655   *
     2656   * This is an internal parameter.
     2657   */
     2658  #define CONFIGURE_MEMORY_FOR_POSIX_SHMS(_shms) \
     2659    _Configure_POSIX_Named_Object_RAM(_shms, sizeof(POSIX_Shm_Control) )
     2660
    26282661
    26292662  #ifdef CONFIGURE_POSIX_INIT_THREAD_TABLE
     
    28522885      CONFIGURE_MEMORY_FOR_POSIX_RWLOCKS( \
    28532886        CONFIGURE_MAXIMUM_POSIX_RWLOCKS) + \
     2887      CONFIGURE_MEMORY_FOR_POSIX_SHMS( \
     2888        CONFIGURE_MAXIMUM_POSIX_SHMS) + \
    28542889      CONFIGURE_MEMORY_FOR_POSIX_TIMERS(CONFIGURE_MAXIMUM_POSIX_TIMERS))
    28552890#else
     
    33343369      CONFIGURE_MAXIMUM_POSIX_BARRIERS,
    33353370      CONFIGURE_MAXIMUM_POSIX_RWLOCKS,
     3371      CONFIGURE_MAXIMUM_POSIX_SHMS,
    33363372      CONFIGURE_POSIX_INIT_THREAD_TABLE_SIZE,
    33373373      CONFIGURE_POSIX_INIT_THREAD_TABLE_NAME
     
    35493585    uint32_t POSIX_BARRIERS;
    35503586    uint32_t POSIX_RWLOCKS;
     3587    uint32_t POSIX_SHMS;
    35513588#endif
    35523589
     
    36023639    CONFIGURE_MEMORY_FOR_POSIX_BARRIERS( CONFIGURE_MAXIMUM_POSIX_BARRIERS ),
    36033640    CONFIGURE_MEMORY_FOR_POSIX_RWLOCKS( CONFIGURE_MAXIMUM_POSIX_RWLOCKS ),
     3641    CONFIGURE_MEMORY_FOR_POSIX_SHMS( CONFIGURE_MAXIMUM_POSIX_SHMS ),
    36043642    CONFIGURE_MEMORY_FOR_POSIX_TIMERS( CONFIGURE_MAXIMUM_POSIX_TIMERS ),
    36053643#endif
     
    36733711       (CONFIGURE_MAXIMUM_POSIX_BARRIERS != 0) || \
    36743712       (CONFIGURE_MAXIMUM_POSIX_RWLOCKS != 0) || \
     3713       (CONFIGURE_MAXIMUM_POSIX_SHMS != 0) || \
    36753714      defined(CONFIGURE_POSIX_INIT_THREAD_TABLE))
    36763715  #error "CONFIGURATION ERROR: POSIX API support not configured!!"
  • cpukit/score/include/rtems/score/objectimpl.h

    r2b442a8e rba776282  
    9393  OBJECTS_POSIX_TIMERS              = 9,
    9494  OBJECTS_POSIX_BARRIERS            = 10,
    95   OBJECTS_POSIX_RWLOCKS             = 11
     95  OBJECTS_POSIX_RWLOCKS             = 11,
     96  OBJECTS_POSIX_SHMS                = 12
    9697} Objects_POSIX_API;
    9798
    9899/** This macro is used to generically specify the last API index. */
    99 #define OBJECTS_POSIX_CLASSES_LAST OBJECTS_POSIX_RWLOCKS
     100#define OBJECTS_POSIX_CLASSES_LAST OBJECTS_POSIX_SHMS
    100101
    101102/*
  • cpukit/score/include/rtems/sysinit.h

    r2b442a8e rba776282  
    5555#define RTEMS_SYSINIT_POSIX_BARRIER              000367
    5656#define RTEMS_SYSINIT_POSIX_RWLOCK               000368
     57#define RTEMS_SYSINIT_POSIX_SHM                  000369
    5758#define RTEMS_SYSINIT_POSIX_CLEANUP              00036a
    5859#define RTEMS_SYSINIT_POSIX_KEYS                 00036b
  • testsuites/psxtests/Makefile.am

    r2b442a8e rba776282  
    1111    psxconcurrency01 psxcond01 psxcond02 psxconfig01 psxenosys \
    1212    psxitimer psxmsgq01 psxmsgq02 psxmsgq03 psxmsgq04 \
    13     psxmutexattr01 psxobj01 psxrwlock01 psxsem01 psxsignal01 psxsignal02 \
    14     psxsignal03 psxsignal04 psxsignal05 psxsignal06 \
     13    psxmutexattr01 psxobj01 psxrwlock01 psxsem01 psxshm01 psxshm02 \
     14    psxsignal01 psxsignal02 psxsignal03 psxsignal04 psxsignal05 psxsignal06 \
    1515    psxspin01 psxsysconf \
    1616    psxtime psxtimer01 psxtimer02 psxualarm psxusleep psxfatal01 psxfatal02 \
  • testsuites/psxtests/configure.ac

    r2b442a8e rba776282  
    189189psxrwlock01/Makefile
    190190psxsem01/Makefile
     191psxshm01/Makefile
     192psxshm02/Makefile
    191193psxsignal01/Makefile
    192194psxsignal02/Makefile
  • testsuites/sptests/spsysinit01/init.c

    r2b442a8e rba776282  
    4444#include <rtems/posix/rwlockimpl.h>
    4545#include <rtems/posix/semaphoreimpl.h>
     46#include <rtems/posix/shmimpl.h>
    4647#include <rtems/posix/timerimpl.h>
    4748#endif /* RTEMS_POSIX_API */
     
    118119  POSIX_RWLOCK_PRE,
    119120  POSIX_RWLOCK_POST,
     121  POSIX_SHM_PRE,
     122  POSIX_SHM_POST,
    120123  POSIX_CLEANUP_PRE,
    121124  POSIX_CLEANUP_POST,
     
    525528}
    526529
     530FIRST(RTEMS_SYSINIT_POSIX_SHM)
     531{
     532  assert(_POSIX_Shm_Information.maximum == 0);
     533  next_step(POSIX_SHM_PRE);
     534}
     535
     536LAST(RTEMS_SYSINIT_POSIX_SHM)
     537{
     538  assert(_POSIX_Shm_Information.maximum != 0);
     539  next_step(POSIX_SHM_POST);
     540}
     541
    527542static size_t user_extensions_pre_posix_cleanup;
    528543
     
    753768#define CONFIGURE_MAXIMUM_POSIX_SEMAPHORES 1
    754769
     770#define CONFIGURE_MAXIMUM_POSIX_SHMS 1
     771
    755772#define CONFIGURE_MAXIMUM_POSIX_TIMERS 1
    756773
Note: See TracChangeset for help on using the changeset viewer.