bringing SDL 1.2.14 from vendor into the main branch
[AROS-Contrib.git] / regina / mt_posix.c
blobfac28f54db0a526244142255bbb8c46a3d6504c8
1 /* This is the file to support POSIX threads.
2 * We initialize the global data structure and the global access variable.
3 */
5 #include "rexx.h"
6 #include <pthread.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <pwd.h>
10 #include <grp.h>
11 #include <time.h>
12 #include <errno.h>
14 typedef struct _MT_mem {
15 struct _MT_mem *prev;
16 struct _MT_mem *next;
17 /* real data follows here */
18 } MT_mem;
20 typedef struct { /* mt_tsd: static variables of this module (thread-safe) */
21 struct group getgrgid_retval;
22 char getgrgid_buf[2048]; /* AT LEAST 1K, better 2K or 4K */
23 struct passwd getpwuid_retval;
24 char getpwuid_buf[2048]; /* AT LEAST 1K, better 2K or 4K */
25 struct tm gmtime_retval;
26 struct tm localtime_retval;
27 #ifdef HAVE_RANDOM_DATA
28 struct random_data random;
29 char random_buf[64]; /* at least sizeof(clock_t) and time_t */
30 int random_init;
31 #endif
32 char strerror_buf[1024];
33 MT_mem * mem_base;
34 } mt_tsd_t; /* thread-specific but only needed by this module. see
35 * ReginaInitializeThread
38 static pthread_key_t ThreadIndex; /* index of the TSD, no initial value */
39 static pthread_once_t ThreadOnce = PTHREAD_ONCE_INIT; /* for pthread_once */
41 /* Deinitialize is called when the thread terminates.
42 * This is a wonderful position to place code which frees all allocated stuff!
44 static void Deinitialize(void *buf)
46 tsd_t *TSD = buf;
47 mt_tsd_t *mt;
48 MT_mem *chunk;
50 if (TSD == NULL) /* Should never happen but be sure */
51 return;
53 deinit_rexxsaa(TSD);
55 mt = TSD->mt_tsd;
56 if (mt)
58 while ((chunk = mt->mem_base) != NULL)
60 TSD->MTFree(TSD,chunk + 1);
61 if (mt->mem_base == chunk)
62 break; /* something goes wrong. Don't run into an endless loop */
65 if (mt)
66 free(mt);
67 free(TSD);
70 /* ThreadGetKey creates a new index key into the TSD table. This function
71 * must only be used by pthread_once.
73 static void ThreadGetKey(void)
75 pthread_key_create(&ThreadIndex,Deinitialize);
78 /* Lowest level memory allocation function for normal circumstances. */
79 static void *MTMalloc(const tsd_t *TSD,size_t size)
81 mt_tsd_t *mt;
82 MT_mem *new = malloc(size + sizeof(MT_mem));
84 if (new == NULL) /* may happen. errors are detected in the above layers */
85 return(NULL);
87 mt = TSD->mt_tsd;
88 new->prev = NULL;
89 new->next = mt->mem_base;
90 if (mt->mem_base)
91 mt->mem_base->prev = new;
92 mt->mem_base = new;
93 return(new + 1); /* jump over the head */
96 /* Lowest level memory deallocation function for normal circumstances. */
97 static void MTFree(const tsd_t *TSD,void *chunk)
99 mt_tsd_t *mt = TSD->mt_tsd;
100 MT_mem *this;
102 this = chunk;
103 this--; /* Go to the header of the chunk */
105 if (this->prev)
106 if (this->prev->next != this)
107 return;
108 if (this->next)
109 if (this->next->prev != this)
110 return;
112 /* This is a chunk allocated by MTMalloc */
113 if (this->prev)
114 this->prev->next = this->next;
115 if (this->next)
116 this->next->prev = this->prev;
117 if (this == mt->mem_base)
118 mt->mem_base = this->next;
120 /* Last not least we set the pointers to NULL. This prevents a double-free*/
121 this->next = NULL;
122 this->prev = NULL;
123 free(this);
126 /* Lowest level exit handler. */
127 static void MTExit(int code)
129 /* pthread_exit's argument has a different semantic as exit has. */
130 pthread_exit(NULL);
133 /* ReginaInitializeThread creates a new thread structure and returns a ptr
134 * to the initialized value.
135 * The function may be called more than once.
137 tsd_t *ReginaInitializeThread(void)
139 int OK;
140 tsd_t *retval;
142 /* get a unique access variable for the whole process */
143 pthread_once(&ThreadOnce,ThreadGetKey);
145 /* fetch the value of the access variable */
146 retval = pthread_getspecific(ThreadIndex);
148 if (retval != NULL)
149 return(retval);
151 /* First call in this thread... */
152 retval = malloc(sizeof(tsd_t)); /* no Malloc, etc! */
154 if (retval == NULL) /* THIS is really a problem. I don't know what we */
155 return(NULL); /* should do now. Let the caller run into a crash... */
157 pthread_setspecific(ThreadIndex,retval);
159 memset(retval,0,sizeof(tsd_t));
160 retval->MTMalloc = MTMalloc;
161 retval->MTFree = MTFree;
162 retval->MTExit = MTExit;
164 /* Since the local data structure contains a memory chain for the memory
165 * management we initialize it first.
167 if ((retval->mt_tsd = malloc(sizeof(mt_tsd_t))) == NULL)
168 return(NULL); /* This is a catastrophy */
169 memset(retval->mt_tsd,0,sizeof(mt_tsd_t));
171 OK = init_memory(retval); /* Initialize the memory module FIRST*/
173 /* Without the initial memory we don't have ANY chance! */
174 if (!OK)
175 return(NULL);
177 OK &= init_vars(retval); /* Initialize the variable module */
178 OK &= init_stacks(retval); /* Initialize the stack module */
179 OK &= init_filetable(retval); /* Initialize the files module */
180 OK &= init_math(retval); /* Initialize the math module */
181 OK &= init_spec_vars(retval); /* Initialize the interprt module */
182 OK &= init_tracing(retval); /* Initialize the tracing module */
183 OK &= init_builtin(retval); /* Initialize the builtin module */
184 OK &= init_client(retval); /* Initialize the client module */
185 OK &= init_library(retval); /* Initialize the library module */
186 OK &= init_rexxsaa(retval); /* Initialize the rexxsaa module */
187 OK &= init_shell(retval); /* Initialize the shell module */
188 OK &= init_envir(retval); /* Initialize the envir module */
189 OK &= init_expr(retval); /* Initialize the expr module */
190 OK &= init_error(retval); /* Initialize the error module */
191 #ifdef VMS
192 OK &= init_vms(retval); /* Initialize the vmscmd module */
193 OK &= init_vmf(retval); /* Initialize the vmsfuncs module */
194 #endif
195 OK |= init_arexxf(&__regina_tsd); /* Initialize the arexxfuncs modules */
196 retval->loopcnt = 1; /* stupid r2perl-module */
197 retval->traceparse = -1;
198 retval->thread_id = (unsigned long)pthread_self();
200 if (!OK)
201 exiterror( ERR_STORAGE_EXHAUSTED, 0 ) ;
203 return(retval);
206 /* __regina_get_tsd returns a pointer to the thread specific data. Be sure to
207 * calls this after a ReginaInitializeThread only.
209 tsd_t *__regina_get_tsd(void)
211 /* See above for comments */
212 return(pthread_getspecific(ThreadIndex));
215 /******************************************************************************
216 ******************************************************************************
217 * Emulation functions to support multi-threading *****************************
218 * The functions will be opaque to the caller *********************************
219 * The "original" functions at this place are not multi-threading safe ********
220 ******************************************************************************
221 *****************************************************************************/
222 #if defined(HAVE_GETGRGID_R_RETURNS_INT_5_PARAMS) || defined(HAVE_GETGRGID_R_RETURNS_INT_4_PARAMS) ||defined(HAVE_GETGRGID_R_RETURNS_STRUCT)
223 /* see documentation of getgrgid and getgrgid_r */
224 struct group *getgrgid(gid_t gid)
226 mt_tsd_t *mt = __regina_get_tsd()->mt_tsd;
227 struct group *ptr;
228 int rc;
229 # ifdef HAVE_GETGRGID_R_RETURNS_INT_5_PARAMS
230 rc = getgrgid_r(gid,
231 &mt->getgrgid_retval,
232 mt->getgrgid_buf,
233 sizeof(mt->getgrgid_buf),
234 &ptr);
235 # endif
236 # ifdef HAVE_GETGRGID_R_RETURNS_INT_4_PARAMS
237 rc = getgrgid_r(gid,
238 &mt->getgrgid_retval,
239 mt->getgrgid_buf,
240 sizeof(mt->getgrgid_buf));
241 ptr = &mt->getgrgid_retval;
242 # endif
243 # ifdef HAVE_GETGRGID_R_RETURNS_STRUCT
244 ptr = getgrgid_r(gid,
245 &mt->getgrgid_retval,
246 mt->getgrgid_buf,
247 sizeof(mt->getgrgid_buf) );
248 # endif
249 if ((rc != 0) || (ptr == NULL))
251 errno = rc;
252 return(NULL);
254 return(ptr);
256 #endif
258 #if defined(HAVE_GETPWUID_R_RETURNS_INT) || defined(HAVE_GETPWUID_R_RETURNS_STRUCT)
259 /* see documentation of getpwuid and getpwuid_r */
260 struct passwd *getpwuid(uid_t uid)
262 mt_tsd_t *mt = __regina_get_tsd()->mt_tsd;
263 struct passwd *ptr;
264 int rc;
266 # ifdef HAVE_GETPWUID_R_RETURNS_INT
267 rc = getpwuid_r(uid,
268 &mt->getpwuid_retval,
269 mt->getpwuid_buf,
270 sizeof(mt->getpwuid_buf),
271 &ptr);
272 # else
273 ptr = getpwuid_r(uid,
274 &mt->getpwuid_retval,
275 mt->getpwuid_buf,
276 sizeof(mt->getpwuid_buf) );
277 # endif
278 if ((rc != 0) || (ptr == NULL))
280 errno = rc;
281 return(NULL);
283 return(ptr);
285 #endif
287 /* see documentation of gmtime and gmtime_r */
288 struct tm *gmtime(const time_t *time)
290 mt_tsd_t *mt = __regina_get_tsd()->mt_tsd;
292 return(gmtime_r(time,&mt->gmtime_retval));
295 /* see documentation of localtime and localtime_r */
296 struct tm *localtime(const time_t *time)
298 mt_tsd_t *mt = __regina_get_tsd()->mt_tsd;
300 return(localtime_r(time,&mt->localtime_retval));
303 #if defined( HAVE_RANDOM_DATA ) && !defined( HAVE_RANDOM )
304 /* see documentation of random and random_r. If you get compile time errors
305 * exclude this function.
307 int32_t random(void)
309 mt_tsd_t *mt = __regina_get_tsd()->mt_tsd;
310 int32_t retval;
312 if (!mt->random_init) /* according to the ANSI this is equivalent to */
313 srandom(1); /* an initial value of 1 for the seed! */
314 random_r(&mt->random,&retval); /* ignore errors, we MUST return anything */
315 return(retval);
318 /* see documentation of srandom and srandom_r. If you get compile time errors
319 * exclude this function.
321 void srandom(unsigned seed)
323 mt_tsd_t *mt = __regina_get_tsd()->mt_tsd;
324 /* The stack is normally not cleared: wonderful random stuff. */
325 char this_is_random_stuff[sizeof(mt->random_buf)];
327 if (!mt->random_init)
329 mt->random_init = 1;
330 /* In case of a cleared new stack seqment we initialize our buffer
331 * with at least one random stuff:
333 time((time_t *) this_is_random_stuff);
334 /* If the buffer is large enough, use a second one */
335 if (sizeof(time_t) + sizeof(clock_t) < sizeof(this_is_random_stuff))
336 *((clock_t *) (this_is_random_stuff + sizeof(time_t))) = clock();
338 memcpy(mt->random_buf,this_is_random_stuff,sizeof(mt->random_buf));
340 initstate_r(seed,
341 mt->random_buf,
342 sizeof(mt->random_buf),
343 &mt->random);
344 /* initstate calls srandom. We can return savely. */
345 return;
347 srandom_r(seed,&mt->random);
349 #endif
351 #ifdef STRERROR_R
352 /* see documentation of strerror and strerror_r. */
353 char *strerror(int errnum)
355 mt_tsd_t *mt = __regina_get_tsd()->mt_tsd;
357 return(strerror_r(errnum,mt->strerror_buf,sizeof(mt->strerror_buf)));
359 #endif