# Shared Memory Applications in C

> As explained in the Shared Memory Applications page, eXtremeDB allows two or more processes to share a common database. General instructions are given there explaining how shared memory segments must be created on different operating systems. Further implementation details for C applications are provided in the following sections.

> ### Important note on multi-process shared memory applications

> In multi-process database applications using shared memory it is necessary to call `mco_runtime_start()` first just by a single process (the primary process). This allows the eXtremeDB runtime to correctly create and initialize the database registry. After the primary process initializes, all other (secondary) processes can call `mco_runtime_start()` simultaneously.

> ## Primary Process Example

> The following code snippet demonstrates how a “primary” process might determine that the correct shared-memory library is linked, then initialize memory devices and open the database in shared-memory:

>         MCO_RET open_shared_db(
>             const char * db_name,  /* name of the database */
>             mco_dictionary_h dict, /* pointer to schema */
>             mco_size_t db_sz,      /* size of memory segment for in-mem part
>                         * of the db */
>             uint2 mem_pg_sz,       /* size of memory page */
>             uint2 max_conn_no      /* max. number of connections */
>         )
>         {
>             mco_runtime_info_t info;
>             mco_db_params_t    db_params;
>             mco_device_t dev;

>             /* get runtime info */
>             mco_get_runtime_info(&info);
>             if (info.mco_shm_supported)
>             {
>                 /* set the device as a shared named memory device */
>                 dev.type       = MCO_MEMORY_NAMED;
>                 sprintf( dev.named.name, "%s-db", db_name ); /* set memory name */
>                 dev.named.flags = 0;       /* zero flags */
>                 dev.named.hint  = 0;       /* set mapping address or null it */
>             }
>             else
>             {
>                 /* Wrong libraries for shared-memory */
>                 return MCO_E_UNSUPPORTED;
>             }

>             dev.assignment = MCO_MEMORY_ASSIGN_DATABASE;   /* main database memory */
>             dev.size       = db_sz;                        /* set the device size */

>             /* initialize and customize the database parameters */
>             mco_db_params_init ( &db_params );    /* initialize the params with default values */
>             db_params.mem_page_size      = mem_pg_sz;
>             db_params.disk_page_size     = 0;
>             db_params.db_max_connections = max_conn_no;

>             /* open a database on the device with given params */
>             return mco_db_open_dev(db_name, dict, dev, 1, &db_params );
>         }

>         void StartDB()
>         {
>             MCO_RET       rc;
>             mco_db_h      db;
>             void* start_mem = 0;

>             /* set fatal error handler *
>             mco_error_set_handler( &errhandler );
>             mco_runtime_start();
>             rc = open_shared_db( dbname, DemoShm1_get_dictionary(),
>                         SEGSZ, PAGESIZE, MAX_CONNECTIONS );
>             if ( MCO_S_OK == rc )
>             {
>                 rc = mco_db_connect( dbname, &db );
>                 /* normal processing goes here */
>                 ...
>                 rc = mco_db_disconnect( db );
>                 rc = mco_db_close( dbname );
>             }
>             mco_runtime_stop();
>         }

> Note that it is important to set the shared memory `flags` and `hint` fields to zero or a valid value in the `mco_device_t` structure passed to `mco_db_open_dev()` as done in the example above:

>                 dev.named.flags = 0;       /* zero flags */
>                 dev.named.hint  = 0;       /* set mapping address or null it */

> ## Secondary Process Example

> Once the shared memory for the database has been set up by `open_shared_db()` in the primary process, subsequent processes will simply connect to the database as demonstrated below:

>         void DbAttach()
>         {
>             MCO_RET       rc;
>             mco_db_h      db;

>             /* set fatal error handler */
>             mco_error_set_handler( &errhandler );
>             mco_runtime_start();

>             /* connect to a database by name "dbname" */
>             rc = mco_db_connect( dbname, &db );
>             if ( MCO_S_OK != rc )
>             {
>                 printf("\n Could not attach to instance: %d\n", rc);
>                 exit( 1 );
>             }

>             /* normal database processing goes here */
>             ...
>             rc = mco_db_disconnect( db );
>             rc = mco_db_close( dbname );

>             mco_runtime_stop();
>         }

> See sample 03-connect_multiprocess for a complete example of a shared memory application.

> ## Shared Memory Runtime Options

> The eXtremeDB runtime can be informed of shared memory configuration through runtime options. The shared memory options are OS specific. To set runtime options that application may call the `mco_runtime_setoption()` before `mco_runtime_start()`. For example:

>         mco_runtime_setoption(MCO_RT_OPTION_UNIX_SHM_MASK, 0600 );

>         rc = mco_runtime_start();
>         ...

> Applications can determine at run-time whether or not the correct eXtremeDB runtime libraries are linked to support shared memory by examining the output of the `mco_get_runtime_info()` function.

> ### For Windows applications

> There are two groups of runtime options: one for determining the scope of the shared memory block name (local or global), and one for determining the security level applied to the shared memory block. The combination of options are applied to the name specified in the shared (named) memory block device.

> For example, the following code snippet creates a shared memory block with the name “shared-db”:

>         if (info.mco_shm_supported)
>         {
>             /* set the device as a shared named memory device */
>             dev.type       = MCO_MEMORY_NAMED;
>             sprintf( dev.named.name, "%s-db", db_name ); /* set memory name */
>             dev.named.flags = 0;       /* zero flags */
>             dev.named.hint  = 0;       /* set mapping address or null it */
>         }

> The default runtime options are as follows:

>         MCO_RT_WINDOWS_SHM_PREFIX_NONE | MCO_RT_WINDOWS_SHM_SEC_DESCR_NULL

> This combination makes the database visible and accessible from only local sessions. To make the database accessible from all sessions of the same user, for example a service and a desktop program running under the same user, the following runtime options are necessary:

>         MCO_RT_WINDOWS_SHM_PREFIX_GLOBAL | MCO_RT_WINDOWS_SHM_SEC_DESCR_SAMEUSER

> To setup the shared memory block with these options the following call must be made prior to calling mco_runtime_start():

>         mco_runtime_setoption( MCO_RT_WINDOWS_SHM_OPT,
>                         MCO_RT_WINDOWS_SHM_PREFIX_GLOBAL |
>                         MCO_RT_WINDOWS_SHM_SEC_DESCR_SAMEUSER );

>         ...
>         rc = mco_runtime_start();
>         ...

> Please note that the value `MCO_RT_WINDOWS_SHM_PREFIX_GLOBAL` is applicable for Services only; desktops sessions are able to create databases using modes

> `MCO_RT_WINDOWS_SHM_PREFIX_LOCAL` or `MCO_RT_WINDOWS_SHM_PREFIX_NONE`; and they can connect to databases opened with `MCO_RT_WINDOWS_SHM_PREFIX_GLOBAL` by using matching security settings.

> ### For Linux applications

> Linux shared memory applications use the POSIX function `mmap()` to provide mapping of virtual shared memory to a current process when the eXtremeDB library `libmcompsx` is used. The option `MCO_RT_POSIX_SHM_ANONYMOUS` enables the option `MAP_ANOUNYMOUS` of function `mmap()`. The option `MCO_RT_POSIX_SHM_SHARED` specifies option `MAP_SHARED` of function `mmap()`, otherwise `MAP_PRIVATE` is used. The default combination of options is

>         MCO_RT_POSIX_SHM_ANONYMOUS | MCO_RT_POSIX_SHM_SHARED.

> To specify different options for the shared memory block call `mco_runtime_setoption()` prior to calling `mco_runtime_start()`. For example to specify `mmap()` option `MAP_PRIVATE`:

>         mco_runtime_setoption( MCO_RT_POSIX_SHM_OPT,
>                         MCO_RT_POSIX_SHM_ANONYMOUS );
>         ...
>         rc = mco_runtime_start();
>         ...

> Note that the Linux AIO library `mcfu98aio` is not designed for shared memory between processes. It may be used for multi-threaded access but not for multi-process sharing of a database.

> ### For Unix applications

> On Unix systems the shared memory access mode is specified by the Unix specific value of file system access rights. Shared memory and semaphores have the same system of permissions as ordinary files to restrict access by processes of different users and groups.

> The default value is `0666` for `MCO_RT_OPTION_UNIX_SHM_MASK` means that `user`, `group` and `others` can read and write this memory or semaphore. A value of `0600` means that only `user` can access (read and write) this memory or semaphore. To change the default to `0600` the following call must be made prior to calling `mco_runtime_start()`:

>         mco_runtime_setoption(MCO_RT_OPTION_UNIX_SHM_MASK, 0600);
>         ...
>         rc = mco_runtime_start();
>         ...

> ## Shared memory and the Direct Pointers library

> When using the eXtremeDB Direct Pointer Arithmetic library (DPTR) it is necessary to map the shared memory segment to the same virtual memory address in every process because in the DPTR implementation eXtremeDB uses actual memory addresses (i.e. it performs pointer arithmetic) to calculate the locations of objects in an eXtremeDB database. The pointers must be the same in every running instance of an eXtremeDB-based application, or pointer arithmetic just doesn’t work. Setting the `hint` parameter to zero causes eXtremeDB to determine the actual shared memory segment address. But this could fail when called from a second process attempting to open the shared database. In this case it is the applications responsibility to provide a valid `hint` address.

> There are several ways to determine where the runtime should map the shared memory database. Developers can use a utility provided by the operating system to gather memory usage information (the process memory map). These utilities will usually display the code, data and stack memory usage of each process running on the system and the libraries it is using. It is necessary to examine the output and pick an address outside any address space.

> In any event, the memory address is an address in each process’s virtual memory. An address should be chosen that is sufficiently far away from the data, stack and heap segment of every process that will share the database. (Hence the default of `0x20000000` for `MAP_ADDRESS`; it is assumed unlikely, but not guaranteed, that any single process will have data, stack and heap that stretch out to `0x20000000` bytes.)

> The above potential issues with respect to the `MAP_ADDRESS` can be avoided by using the `Offset` library instead of the Direct Pointer Arithmetic library. The `Offset` approach calculates an offset from the beginning address of the in-memory database, to locate objects. Therefore, it does not depend on the in-memory database starting at a common (and known) location for all processes. However, the DPTR pointer arithmetic is about 5%–15% faster than calculating offsets.