summaryrefslogtreecommitdiff
path: root/docs/tutorials/021/server.cpp
blob: 049e893049d35881acdf48103eb38cc7f63a9404 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156

// $Id$

/*
  I've hidden the details in an Allocator class declared in mpool.h
  We'll come to that a little later.
*/
#include "mpool.h"

#if defined(ACE_LACKS_SYSV_SHMEM)
int
main (int, char *[])
{
  ACE_ERROR_RETURN ((LM_ERROR,
                     "System V Semaphores not available on this platform.\n"),100);
}
#else // ACE_LACKS_SYSV_SHMEM
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;
}

/*
  This tutorial was created by shamelessly modifying one of the ACE
  examples.  Someone there had already created the necessary explicit
  template instantiations & I don't want them to go to waste...
 */
#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION)
template class ACE_Malloc<ACE_MMAP_MEMORY_POOL, ACE_SV_Semaphore_Simple>;
template class ACE_Guard<ACE_SV_Semaphore_Simple>;
template class ACE_Write_Guard<ACE_SV_Semaphore_Simple>;
template class ACE_Read_Guard<ACE_SV_Semaphore_Simple>;
#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA)
#pragma instantiate ACE_Malloc<ACE_MMAP_MEMORY_POOL, ACE_SV_Semaphore_Simple>
#pragma instantiate ACE_Guard<ACE_SV_Semaphore_Simple>
#pragma instantiate ACE_Write_Guard<ACE_SV_Semaphore_Simple>
#pragma instantiate ACE_Read_Guard<ACE_SV_Semaphore_Simple>
#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */

#endif /* ACE_LACKS_SYSV_SHMEM */