©2004 by Marc J. Rochkind. All rights reserved. Portions marked "Open Source" may be copied under license.

 

Main Page   Modules   Namespace List   Class Hierarchy   Compound List   File List   Namespace Members   Compound Members   File Members  

c7/smi_shm.c

Go to the documentation of this file.
00001 /*
00002     SMI - System V shared memory
00003     AUP2, Sec. 7.13.3
00004 
00005     Copyright 2003 by Marc J. Rochkind. All rights reserved.
00006     May be copied only for purposes and under conditions described
00007     on the Web page www.basepath.com/aup/copyright.htm.
00008 
00009     The Example Files are provided "as is," without any warranty;
00010     without even the implied warranty of merchantability or fitness
00011     for a particular purpose. The author and his publisher are not
00012     responsible for any damages, direct or incidental, resulting
00013     from the use or non-use of these Example Files.
00014 
00015     The Example Files may contain defects, and some contain deliberate
00016     coding mistakes that were included for educational reasons.
00017     You are responsible for determining if and how the Example Files
00018     are to be used.
00019 
00020 */
00021 #include "defs.h"
00022 #include "smi_shm.h"
00023 #include <sys/sem.h>
00024 #include <sys/shm.h>
00025 /*[define]*/
00026 #define SEMI_READ   0
00027 #define SEMI_WRITE  1
00028 #define SEMI_POST   1
00029 #define SEMI_WAIT   -1
00030 /*[SMIQ_SHM]*/
00031 typedef struct {
00032     SMIENTITY sq_entity;          /* entity */
00033     int sq_semid_server;          /* server sem */
00034     int sq_semid_client;          /* client sem (client only) */
00035     int sq_shmid_server;          /* server shm ID */
00036     int sq_shmid_client;          /* client shm ID (client only) */
00037     struct smi_msg *msg_server;   /* ptr to server shm */
00038     struct smi_msg *msg_client;   /* ptr to client shm (client only) */
00039     char sq_name[SERVER_NAME_MAX];/* server name */
00040     struct client_id sq_client;   /* client identification (server only)*/
00041 } SMIQ_SHM;
00042 /*[]*/
00043 #ifndef BSD_DERIVED
00044 union semun { /* not in sys/sem.h, except in FreeBSD and Darwin */
00045     int val;
00046     struct semid_ds *buf;
00047     unsigned short *array;
00048 };
00049 #endif
00050 
00051 /*[mkshm_name_server]*/
00052 static void mkshm_name_server(const SMIQ_SHM *p, char *shmname,
00053   size_t shmname_max)
00054 {
00055     snprintf(shmname, shmname_max, "/tmp/smishm-%s", p->sq_name);
00056 }
00057 /*[op_semi]*/
00058 static int op_semi(int semid, int sem_num, int sem_op)
00059 {
00060     struct sembuf sbuf;
00061     int r;
00062 
00063     sbuf.sem_num = sem_num;
00064     sbuf.sem_op = sem_op;
00065     sbuf.sem_flg = 0;
00066     ec_neg1( r = semop(semid, &sbuf, 1) )
00067     return r;
00068 
00069 EC_CLEANUP_BGN
00070     return -1;
00071 EC_CLEANUP_END
00072 }
00073 /*[init_semi]*/
00074 static int init_semi(int semid)
00075 {
00076     union semun arg;
00077     int r;
00078 
00079     arg.val = 0;
00080     semctl(semid, SEMI_WRITE, SETVAL, arg);
00081     semctl(semid, SEMI_READ, SETVAL, arg);
00082     /* Following call will set otime, allowing clients to proceed. */
00083     ec_neg1( r = op_semi(semid, SEMI_WRITE, SEMI_POST) )
00084     return r;
00085 
00086 EC_CLEANUP_BGN
00087     return -1;
00088 EC_CLEANUP_END
00089 }
00090 /*[]*/
00091 /*
00092     Following function used only by server.
00093 */
00094 /*[getaddr]*/
00095 #define MAX_ATTACHED 20
00096 
00097 static void *getaddr(int shmid)
00098 {
00099     static struct {
00100         int shmid;
00101         void *addr;
00102         long time;
00103     } mapped[MAX_ATTACHED];
00104     static long clock;
00105     int i, avail, oldest, r;
00106     static bool first = true;
00107 
00108     if (first) {
00109         first = false;
00110         for (i = 0; i < MAX_ATTACHED; i++)
00111             mapped[i].shmid = -1;
00112     }
00113     if (shmid == -1) { /* final cleanup */
00114         for (i = 0; i < MAX_ATTACHED; i++)
00115             if (mapped[i].shmid != -1) {
00116                 (void)shmdt(mapped[i].addr);
00117                 mapped[i].shmid = -1;
00118             }
00119         return NULL;
00120     }
00121     clock++;
00122     avail = -1;
00123     for (i = 0; i < MAX_ATTACHED; i++) {
00124         if (mapped[i].shmid == shmid) {
00125             mapped[i].time = clock;
00126             return mapped[i].addr;
00127         }
00128         if (mapped[i].shmid == -1 && avail == -1)
00129             avail = i;
00130     }
00131     if (avail == -1) { /* all fds in use; find oldest */
00132         oldest = -1;
00133         for (i = 0; i < MAX_ATTACHED; i++)
00134             if (oldest == -1 || mapped[i].time < oldest) {
00135                 oldest = mapped[i].time;
00136                 avail = i;
00137             }
00138         r = shmdt(mapped[avail].addr);
00139         ec_neg1( r )
00140     }
00141     mapped[avail].addr = shmat(shmid, NULL, 0);
00142     ec_neg1( mapped[avail].addr )
00143     mapped[avail].time = clock;
00144     mapped[avail].shmid = shmid;
00145     return mapped[avail].addr;
00146 
00147 EC_CLEANUP_BGN
00148     return NULL;
00149 EC_CLEANUP_END
00150 }
00151 /*[]*/
00152 /* cleanup needs to be more complete than just free(p) */
00153 /*[smi_open_shm]*/
00154 SMIQ *smi_open_shm(const char *name, SMIENTITY entity, size_t msgsize)
00155 {
00156     SMIQ_SHM *p = NULL;
00157     char shmname[FILENAME_MAX];
00158     int i;
00159     key_t key;
00160 
00161     ec_null( p = calloc(1, sizeof(SMIQ_SHM)) )
00162     p->sq_entity = entity;
00163     if (strlen(name) >= SERVER_NAME_MAX) {
00164         errno = ENAMETOOLONG;
00165         EC_FAIL
00166     }
00167     strcpy(p->sq_name, name);
00168     mkshm_name_server(p, shmname, sizeof(shmname));
00169     (void)close(open(shmname, O_WRONLY | O_CREAT, 0));
00170     ec_neg1( key = ftok(shmname, 1) )
00171     if (p->sq_entity == SMI_SERVER) {
00172         if ((p->sq_semid_server = semget(key, 2, PERM_FILE)) != -1)
00173             (void)shmctl(p->sq_semid_server, IPC_RMID, NULL);
00174         ec_neg1( p->sq_semid_server = semget(key, 2,
00175           PERM_FILE | IPC_CREAT) )
00176         p->sq_semid_client = -1;
00177         if ((p->sq_shmid_server = shmget(key, 0, PERM_FILE)) != -1)
00178             (void)shmctl(p->sq_shmid_server, IPC_RMID, NULL);
00179         ec_neg1( p->sq_shmid_server = shmget(key, msgsize,
00180           PERM_FILE | IPC_CREAT) )
00181         p->sq_shmid_client = -1;
00182         ec_neg1( init_semi(p->sq_semid_server) )
00183     }
00184     else {
00185         ec_neg1( p->sq_semid_server = semget(key, 2, PERM_FILE) )
00186         ec_neg1( p->sq_semid_client = semget(IPC_PRIVATE, 2,
00187           PERM_FILE | IPC_CREAT) )
00188         ec_neg1( p->sq_shmid_server = shmget(key, msgsize, PERM_FILE) )
00189         ec_neg1( p->sq_shmid_client = shmget(IPC_PRIVATE, msgsize,
00190           PERM_FILE | IPC_CREAT) )
00191         ec_neg1( p->msg_client = shmat(p->sq_shmid_client, NULL, 0) )
00192         ec_neg1( init_semi(p->sq_semid_client) )
00193 /*[]*/
00194         /*
00195             Wait until server has initialized its semaphores.
00196 
00197             Two problems with this loop:
00198                 1. Wait of 1 sec or more messes up performance testing.
00199                 2. Some systems (e.g., FreeBSD) seem not to update otime.
00200 
00201             Solutions:
00202                 1. Mode that skips the loop.
00203                 2. Loop for at most 10 sec. If it hasn't happened by then,
00204                    it most likely never will.
00205         */
00206 /*[smi_open_shm2]*/
00207         for (i = 0; !smi_client_nowait && i < 10; i++) {
00208             union semun arg;
00209             struct semid_ds ds;
00210 
00211             arg.buf = &ds;
00212             ec_neg1( semctl(p->sq_semid_server, SEMI_WRITE, IPC_STAT,
00213               arg) )
00214             if (ds.sem_otime > 0)
00215                 break;
00216             sleep(1);
00217         }
00218     }
00219     ec_neg1( p->msg_server = shmat(p->sq_shmid_server, NULL, 0) )
00220     return (SMIQ *)p;
00221 
00222 EC_CLEANUP_BGN
00223     free(p);
00224     return NULL;
00225 EC_CLEANUP_END
00226 }
00227 /*[smi_close_shm]*/
00228 bool smi_close_shm(SMIQ *sqp)
00229 {
00230     SMIQ_SHM *p = (SMIQ_SHM *)sqp;
00231 
00232     if (p->sq_entity == SMI_SERVER) {
00233         char shmname[FILENAME_MAX];
00234 
00235         (void)getaddr(-1);
00236 #ifdef DARWIN /* not in book */
00237 {
00238         union semun dummy;
00239         ec_neg1( semctl(p->sq_semid_server, 0, IPC_RMID, dummy) )
00240 }
00241 #else
00242         ec_neg1( semctl(p->sq_semid_server, 0, IPC_RMID) )
00243 #endif
00244         (void)shmdt(p->msg_server);
00245         (void)shmctl(p->sq_shmid_server, IPC_RMID, NULL);
00246         mkshm_name_server(p, shmname, sizeof(shmname));
00247         (void)unlink(shmname);
00248     }
00249     else {
00250         /* following call to semctl causes msg_time to hang on last client with FreeBSD */
00251 #ifndef FREEBSD
00252 #ifdef DARWIN /* not in book */
00253 {
00254         union semun dummy;
00255         ec_neg1( semctl(p->sq_semid_client, 0, IPC_RMID, dummy) )
00256 }
00257 #else
00258         ec_neg1( semctl(p->sq_semid_client, 0, IPC_RMID) )
00259 #endif
00260 #endif
00261         (void)shmdt(p->msg_server);
00262         (void)shmdt(p->msg_client);
00263         (void)shmctl(p->sq_shmid_client, IPC_RMID, NULL);
00264     }
00265     free(p);
00266     return true;
00267 
00268 EC_CLEANUP_BGN
00269     return false;
00270 EC_CLEANUP_END
00271 }
00272 /*[]*/
00273 /*[smi_send_getaddr_shm]*/
00274 bool smi_send_getaddr_shm(SMIQ *sqp, struct client_id *client, void **addr)
00275 {
00276     SMIQ_SHM *p = (SMIQ_SHM *)sqp;
00277     int semid_receiver;
00278 
00279     if (p->sq_entity == SMI_SERVER) {
00280         semid_receiver = client->c_id2;
00281         p->sq_client = *client;
00282     }
00283     else
00284         semid_receiver = p->sq_semid_server;
00285     ec_neg1( op_semi(semid_receiver, SEMI_WRITE, SEMI_WAIT) )
00286     if (p->sq_entity == SMI_SERVER)
00287         ec_null( *addr = getaddr(client->c_id1) )
00288     else {
00289         *addr = p->msg_server;
00290         ((struct smi_msg *)*addr)->smi_client.c_id1 = p->sq_shmid_client;
00291         ((struct smi_msg *)*addr)->smi_client.c_id2 = p->sq_semid_client;
00292     }
00293     return true;
00294 
00295 EC_CLEANUP_BGN
00296     return false;
00297 EC_CLEANUP_END
00298 }
00299 /*[smi_send_release_shm]*/
00300 bool smi_send_release_shm(SMIQ *sqp)
00301 {
00302     SMIQ_SHM *p = (SMIQ_SHM *)sqp;
00303     int semid_receiver;
00304 
00305     if (p->sq_entity == SMI_SERVER)
00306         semid_receiver = p->sq_client.c_id2;
00307     else
00308         semid_receiver = p->sq_semid_server;
00309     ec_neg1( op_semi(semid_receiver, SEMI_READ, SEMI_POST) )
00310     return true;
00311 
00312 EC_CLEANUP_BGN
00313     return false;
00314 EC_CLEANUP_END
00315 }
00316 /*[smi_receive_getaddr_shm]*/
00317 bool smi_receive_getaddr_shm(SMIQ *sqp, void **addr)
00318 {
00319     SMIQ_SHM *p = (SMIQ_SHM *)sqp;
00320     int semid_receiver;
00321 
00322     if (p->sq_entity == SMI_SERVER)
00323         semid_receiver = p->sq_semid_server;
00324     else
00325         semid_receiver = p->sq_semid_client;
00326     ec_neg1( op_semi(semid_receiver, SEMI_READ, SEMI_WAIT) )
00327     if (p->sq_entity == SMI_SERVER)
00328         *addr = p->msg_server;
00329     else
00330         *addr = p->msg_client;
00331     return true;
00332 
00333 EC_CLEANUP_BGN
00334     return false;
00335 EC_CLEANUP_END
00336 }
00337 /*[smi_receive_release_shm]*/
00338 bool smi_receive_release_shm(SMIQ *sqp)
00339 {
00340     SMIQ_SHM *p = (SMIQ_SHM *)sqp;
00341     int semid_receiver;
00342 
00343     if (p->sq_entity == SMI_SERVER)
00344         semid_receiver = p->sq_semid_server;
00345     else
00346         semid_receiver = p->sq_semid_client;
00347     ec_neg1( op_semi(semid_receiver, SEMI_WRITE, SEMI_POST) )
00348     return true;
00349 
00350 EC_CLEANUP_BGN
00351     return false;
00352 EC_CLEANUP_END
00353 }
00354 /*[]*/

Generated on Fri Apr 23 10:57:00 2004 for AUP2 Example Source by doxygen 1.3.1