2002-05-05 Dick Porter <dick@ximian.com>
[mono.git] / mono / io-layer / shared.c
1 /*
2  * Code to support inter-process sharing of handles.
3  *
4  * I thought of using an mmap()ed file for this.  If linuxthreads
5  * supported PTHREAD_PROCESS_SHARED I would have done; however without
6  * that pthread support the only other inter-process IPC
7  * synchronisation option is a sysV semaphore, and if I'm going to use
8  * that I may as well take advantage of sysV shared memory too.
9  * Actually, semaphores seem to be buggy, or I was using them
10  * incorrectly :-).  I've replaced the sysV semaphore with a shared
11  * integer controlled with Interlocked functions.
12
13  * mmap() files have the advantage of avoiding namespace collisions,
14  * but have the disadvantage of needing cleaning up, and also msync().
15  * sysV shared memory has a really stupid way of getting random key
16  * IDs, which can lead to collisions.
17  *
18  * I deliberately don't ever delete the shared memory: I'd like to
19  * have been able to set the shared memory segment to destroy itself
20  * on last close, but it doesn't support that. (Setting IPC_RMID on a
21  * segment causes subsequent shmat() with the same key to get a new
22  * segment :-( ).  The function to delete the shared memory segment is
23  * only called from a debugging tool (mono/handles/shmdel).
24  *
25  * w32 processes do not have the POSIX parent-child relationship, so a
26  * process handle is available to any other process to find out exit
27  * status.  Handles are destroyed when the last reference to them is
28  * closed.  New handles can be created for long lasting items such as
29  * processes or threads, and also for named synchronisation objects so
30  * long as these haven't been deleted by having the last referencing
31  * handle closed.
32  */
33
34 #include <config.h>
35 #include <glib.h>
36 #include <stdio.h>
37 #include <unistd.h>
38 #include <sys/types.h>
39 #include <sys/ipc.h>
40 #include <sys/shm.h>
41 #include <errno.h>
42 #include <string.h>
43
44 #include <mono/io-layer/wapi.h>
45 #include <mono/io-layer/wapi-private.h>
46 #include <mono/io-layer/shared.h>
47
48 #undef DEBUG
49
50 gpointer _wapi_shm_attach (gboolean daemon)
51 {
52         gpointer shm_seg;
53         int shm_id;
54         key_t key;
55         gboolean fork_daemon=FALSE;
56         struct _WapiHandleShared_list *data;
57
58         /*
59          * This is an attempt to get a unique key id.  The first arg
60          * to ftok is a path, so when the config file support is done
61          * we should use that.
62          */
63         key=ftok (g_get_home_dir (), _WAPI_HANDLE_VERSION);
64         
65 try_again:
66         shm_id=shmget (key, sizeof(struct _WapiHandleShared_list)+
67                        _WAPI_SHM_SCRATCH_SIZE, IPC_CREAT | IPC_EXCL | 0600);
68         if(shm_id==-1 && errno==EEXIST) {
69                 /* Cool, we dont have to fork the handle daemon, but
70                  * we still need to try and get the shm_id.
71                  */
72                 shm_id=shmget (key, 0, 0600);
73                         
74                 /* it's possible that the shared memory segment was
75                  * deleted in between seeing if it exists, and
76                  * attaching it.  If we got an error here, just try
77                  * attaching it again.
78                  */
79                 if(shm_id==-1) {
80                         goto try_again;
81                 }
82         } else if (shm_id!=-1) {
83                 /* We created the shared memory segment, so we need to
84                  * fork the handle daemon too
85                  */
86                 fork_daemon=TRUE;
87
88                 /* sysv shared mem is set to all zero when allocated,
89                  * so we don't need to do any more initialisation here
90                  */
91         } else {
92                 /* Some error other than EEXIST */
93                 g_message (G_GNUC_PRETTY_FUNCTION ": shmget error: %s",
94                            strerror (errno));
95                 exit (-1);
96         }
97         
98         /* From now on, we need to delete the shm segment before
99          * exiting on error if we created it (ie, if
100          * fork_daemon==TRUE)
101          */
102         shm_seg=shmat (shm_id, NULL, 0);
103         if(shm_seg==(gpointer)-1) {
104                 g_message (G_GNUC_PRETTY_FUNCTION ": shmat error: %s",
105                            strerror (errno));
106                 if(fork_daemon==TRUE) {
107                         _wapi_shm_destroy ();
108                 }
109                 exit (-1);
110         }
111
112         if(daemon==TRUE) {
113                 /* No more to do in the daemon */
114                 return(shm_seg);
115         }
116                 
117         data=shm_seg;
118
119         if(fork_daemon==TRUE) {
120                 pid_t pid;
121                         
122                 pid=fork ();
123                 if(pid==-1) {
124                         g_message (G_GNUC_PRETTY_FUNCTION ": fork error: %s",
125                                    strerror (errno));
126                         _wapi_shm_destroy ();
127                         exit (-1);
128                 } else if (pid==0) {
129                         /* child */
130                         setsid ();
131                         execl (MONO_BINDIR "/mono-handle-d", "mono-handle-d",
132                                NULL);
133                         g_message (G_GNUC_PRETTY_FUNCTION ": exec error: %s",
134                                    strerror (errno));
135                         data->daemon_running=2;
136                         exit (-1);
137                 }
138                 /* parent carries on */
139         }
140                 
141         /* Set up the handle daemon connection */
142
143         while(data->daemon_running==0) {
144                 /* wait for the daemon to sort itself out.  To be
145                  * completely safe, we should have a timeout before
146                  * giving up.
147                  */
148                 struct timespec sleepytime;
149                         
150                 sleepytime.tv_sec=0;
151                 sleepytime.tv_nsec=10000000;    /* 10ms */
152                         
153                 nanosleep (&sleepytime, NULL);
154         }
155         if(data->daemon_running==2) {
156                 /* Oh dear, the daemon had an error starting up */
157                 if(fork_daemon==TRUE) {
158                         _wapi_shm_destroy ();
159                 }
160                 g_error ("Handle daemon failed to start");
161         }
162                 
163         /* From now on, it's up to the daemon to delete the shared
164          * memory segment
165          */
166
167         return(shm_seg);
168 }
169
170 void _wapi_shm_destroy (void)
171 {
172 #ifndef DISABLE_SHARED_HANDLES
173         int shm_id;
174         key_t key;
175                 
176         /*
177          * This is an attempt to get a unique key id.  The
178          * first arg to ftok is a path, so when the config
179          * file support is done we should use that.
180          */
181         key=ftok (g_get_home_dir (), _WAPI_HANDLE_VERSION);
182         
183         shm_id=shmget (key, 0, 0600);
184         if(shm_id==-1 && errno==ENOENT) {
185                 return;
186         } else if (shm_id==-1) {
187                 g_message (G_GNUC_PRETTY_FUNCTION ": shmget error: %s",
188                            strerror (errno));
189                 exit (-1);
190         }
191         if(shmctl (shm_id, IPC_RMID, NULL)==-1) {
192                 g_message (G_GNUC_PRETTY_FUNCTION ": shmctl error: %s",
193                            strerror (errno));
194                 exit (-1);
195         }
196 #endif /* DISABLE_SHARED_HANDLES */
197 }