static int child (char *shm) { // Both semaphores are initially created with a count of 0, i.e., // they are "locked." ACE_SV_Semaphore_Complex mutex (SEM_KEY_1, ACE_SV_Semaphore_Complex::ACE_CREATE, 0); ACE_SV_Semaphore_Complex synch (SEM_KEY_2, ACE_SV_Semaphore_Complex::ACE_CREATE, 0); // Perform "busy waiting" here until we acquire the semaphore. This // isn't really a good design -- it's just to illustrate that you // can do non-blocking acquire() calls with the ACE System V // semaphore wrappers. while (mutex.tryacquire () == -1) if (errno == EAGAIN) ACE_DEBUG ((LM_DEBUG, "spinning in child!\n")); else ACE_ERROR_RETURN ((LM_ERROR, "child mutex.tryacquire"), 1); for (char *s = (char *) shm; *s != '\0'; s++) ACE_DEBUG ((LM_DEBUG, "%c", *s)); ACE_DEBUG ((LM_DEBUG, "\n")); if (synch.release () == -1) ACE_ERROR_RETURN ((LM_ERROR, "child synch.release"), 1); return 0; }
static int child (char *shm) { ACE_SV_Semaphore_Complex sem (SEM_KEY, ACE_SV_Semaphore_Complex::ACE_CREATE, 0, 2); while (sem.tryacquire (0) == -1) if (errno == EAGAIN) ACE_DEBUG ((LM_DEBUG, "spinning in client!\n")); else ACE_ERROR_RETURN ((LM_ERROR, "client mutex.tryacquire(0)"), 1); for (char *s = (char *) shm; *s != '\0'; s++) ACE_DEBUG ((LM_DEBUG, "%c", *s)); ACE_DEBUG ((LM_DEBUG, "\n")); if (sem.release (1) < 0) ACE_ERROR ((LM_ERROR, "client sem.release(1)")); return 0; }
static int parent (char *shm) { char *s = shm; // Both semaphores are initially created with a count of 0, i.e., // they are "locked." ACE_SV_Semaphore_Complex mutex (SEM_KEY_1, ACE_SV_Semaphore_Complex::ACE_CREATE, 0); ACE_SV_Semaphore_Complex synch (SEM_KEY_2, ACE_SV_Semaphore_Complex::ACE_CREATE, 0); // This is a critical section, which is protected by the mutex // semaphore. for (char c = 'a'; c <= 'z'; c++) *s++ = c; *s = '\0'; if (mutex.release () == -1) ACE_ERROR ((LM_ERROR, "%p", "parent mutex.release")); else if (synch.acquire () == -1) ACE_ERROR ((LM_ERROR, "%p", "parent synch.acquire")); if (my_alloc.remove () == -1) ACE_ERROR ((LM_ERROR, "%p\n", "my_alloc.remove")); if (mutex.remove () == -1) ACE_ERROR ((LM_ERROR, "%p\n", "mutex.remove")); if (synch.remove () == -1) ACE_ERROR ((LM_ERROR, "%p\n", "synch.remove")); return 0; }
static int parent (char *shm) { char *s = shm; ACE_SV_Semaphore_Complex sem (SEM_KEY, ACE_SV_Semaphore_Complex::ACE_CREATE, 0, 2); for (char c = 'a'; c <= 'z'; c++) *s++ = c; *s = '\0'; if (sem.release (0) == -1) ACE_ERROR ((LM_ERROR, "%p", "parent sem.release(0)")); else if (sem.acquire (1) == -1) ACE_ERROR ((LM_ERROR, "%p", "parent sem.acquire(1)")); if (alloc.remove () == -1) ACE_ERROR ((LM_ERROR, "%p\n", "alloc.remove")); if (sem.remove () == -1) ACE_ERROR ((LM_ERROR, "%p\n", "sem.remove")); return 0; }
static int child (char *shm) { int result; ACE_SV_Semaphore_Complex mutex; // This semaphore is initially created with a count of 0, i.e., it // is "locked." result = mutex.open (SEM_KEY_1, ACE_SV_Semaphore_Complex::ACE_CREATE, 0); ACE_ASSERT (result != -1); ACE_SV_Semaphore_Complex synch; // This semaphore is initially created with a count of 0, i.e., it // is "locked." result = synch.open (SEM_KEY_2, ACE_SV_Semaphore_Complex::ACE_CREATE, 0); ACE_ASSERT (result != -1); // Perform "busy waiting" here until we acquire the semaphore. This // isn't really a good design -- it's just to illustrate that you // can do non-blocking acquire() calls with the ACE System V // semaphore wrappers. while ((result = mutex.tryacquire ()) == -1) if (errno == EAGAIN) ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P) spinning in child!\n"))); else { ACE_ERROR ((LM_ERROR, ACE_TEXT ("(%P) child mutex.tryacquire"))); ACE_ASSERT (result != -1); } for (int i = 0; i < SHMSZ; i++) ACE_ASSERT (SHMDATA[i] == shm[i]); result = synch.release (); ACE_ASSERT (result != -1); return 0; }
int main (int, char *[]) { /* Construction of an Allocator will create the memory pool and provide it with a name. The Constants class is also declared in mpool.h to keep server and client on the same page. The name is used to generate a unique semaphore which prevents simultaneous access to the pools housekeeping information. (Note that you still have to provide your own synch mechanisms for the data *you* put in the poo.) */ Allocator allocator (Constants::PoolName); /* The Allocator class provides the pool() member so that you have access to the actual memory pool. A more robust implementation would behave more as a bridge class but this is good enough for what we're doing here. Once you have a reference to the pool, the malloc() method can be used to get some bytes. If successful, shm will point to the data. Otherwise, it will be zero. */ char *shm = (char *) allocator.pool ().malloc (27); ACE_ASSERT (shm != 0); /// FYI ACE_DEBUG ((LM_INFO, "Shared memory is at 0x%x\n", shm)); /* Something that we can do with a memory pool is map a name to a region provided by malloc. By doing this, we can communicate that name to the client as a rendezvous location. Again, a member of Constants is used to keep the client and server coordinated. */ if (allocator.pool ().bind(Constants::RegionName,shm) == -1) ACE_ERROR_RETURN ((LM_ERROR, "Cannot bind the name '%s' to the pointer 0x%x\n", Constants::RegionName, shm), 100); /* One of the best ways to synch between different processes is through the use of semaphores. ACE_SV_Semaphore_Complex hides the gory details and lets us use them rather easily. Here, we'll create two semaphores: mutex and synch. mutex will be used to provide mutually exclusive access to the shared region for writting/reading. synch will be used to prevent the server from removing the memory pool before the client is done with it. Both semaphores are created in an initially locked state. */ ACE_SV_Semaphore_Complex mutex; ACE_ASSERT (mutex.open (Constants::SEM_KEY_1, ACE_SV_Semaphore_Complex::ACE_CREATE, 0) != -1); ACE_SV_Semaphore_Complex synch; ACE_ASSERT (synch.open (Constants::SEM_KEY_2, ACE_SV_Semaphore_Complex::ACE_CREATE, 0) != -1); /* We know the mutex is locked because we created it that way. Take a moment to write some data into the shared region. */ for (int i = 0; i < Constants::SHMSZ; i++) shm[i] = Constants::SHMDATA[i]; /* The client will be blocking on an acquire() of mutex. By releasing it here, the client can go look at the shared data. */ if (mutex.release () == -1) ACE_ERROR ((LM_ERROR, "(%P) %p", "server mutex.release")); /* Even though we created the synch semaphore in a locked state, if we attempt to acquire() it, we will block. Our design requires that the client release() synch when it is OK for us to remove the shared memory. */ else if (synch.acquire () == -1) ACE_ERROR ((LM_ERROR, "(%P) %p", "server synch.acquire")); /* This will remove all of the memory pool's resources. In the case where a memory mapped file is used, the physical file will also be removed. */ if (allocator.pool ().remove () == -1) ACE_ERROR ((LM_ERROR, "(%P) %p\n", "server allocator.remove")); /* We now have to cleanup the semaphores we created. Use the ipcs command to see that they did, indeed, go away after the server exits. */ if (mutex.remove () == -1) ACE_ERROR ((LM_ERROR, "(%P) %p\n", "server mutex.remove")); else if (synch.remove () == -1) ACE_ERROR ((LM_ERROR, "(%P) %p\n", "server synch.remove")); return 0; }