1 /*
2
3 MPDM - Minimum Profit Data Manager
4 mpdm_t.c - Threads, mutexes, semaphores and other stuff
5
6 ttcdt <dev@triptico.com> et al.
7
8 This software is released into the public domain.
9 NO WARRANTY. See file LICENSE for details.
10
11 */
12
13 #include "config.h"
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <locale.h>
19 #include <time.h>
20
21 #ifdef CONFOPT_WIN32
22 #include <windows.h>
23 #endif
24
25 #ifdef CONFOPT_PTHREADS
26 #include <pthread.h>
27 #endif
28
29 #ifdef CONFOPT_POSIXSEMS
30 #include <semaphore.h>
31 #endif
32
33 #ifdef CONFOPT_GETTIMEOFDAY
34 #include <sys/time.h>
35 #endif
36
37 #ifdef CONFOPT_ZLIB
38 #include <zlib.h>
39 #endif
40
41 #include "mpdm.h"
42
43
44 /** code **/
45
46 /**
47 * mpdm_sleep - Sleeps a number of milliseconds.
48 * @msecs: the milliseconds to sleep
49 *
50 * Sleeps a number of milliseconds.
51 * [Threading]
52 */
mpdm_sleep(int msecs)53 void mpdm_sleep(int msecs)
54 {
55 #ifdef CONFOPT_WIN32
56
57 Sleep(msecs);
58
59 #endif
60
61 #ifdef CONFOPT_NANOSLEEP
62 struct timespec ts;
63
64 ts.tv_sec = msecs / 1000;
65 ts.tv_nsec = (msecs % 1000) * 1000000;
66
67 nanosleep(&ts, NULL);
68 #endif
69 }
70
71
mpdm_time(void)72 double mpdm_time(void)
73 {
74 double r = 0.0;
75
76 #ifdef CONFOPT_GETTIMEOFDAY
77
78 struct timeval tv;
79
80 gettimeofday(&tv, NULL);
81
82 r = ((double)tv.tv_sec) + (((double)tv.tv_usec) / 1000000.0);
83
84 #else /* CONFOPT_GETTIMEOFDAY */
85
86 r = (double) time(NULL);
87
88 #endif /* CONFOPT_GETTIMEOFDAY */
89
90 return r;
91 }
92
93
mpdm_random(mpdm_t v)94 mpdm_t mpdm_random(mpdm_t v)
95 {
96 static unsigned int seed = 0;
97 mpdm_t r = NULL;
98
99 if (mpdm_type(v) == MPDM_TYPE_ARRAY)
100 r = mpdm_get(v, mpdm_random(MPDM_I(mpdm_size(v))));
101 else {
102 int range = mpdm_ival(v);
103 int v = 0;
104
105 /* crappy random seed */
106 if (seed == 0) {
107 time_t t = time(NULL);
108 seed = t ^ (t << 16);
109 }
110
111 seed = (seed * 58321) + 11113;
112 v = (seed >> 16);
113
114 if (range)
115 r = MPDM_I(v % range);
116 else
117 r = MPDM_R((v & 0xffff) / 65536.0);
118 }
119
120 return r;
121 }
122
123
124 /** mutexes **/
125
vc_mutex_destroy(mpdm_t v)126 static mpdm_t vc_mutex_destroy(mpdm_t v)
127 {
128 #ifdef CONFOPT_WIN32
129 HANDLE *h = (HANDLE *) v->data;
130
131 CloseHandle(*h);
132 #endif
133
134 #ifdef CONFOPT_PTHREADS
135 pthread_mutex_t *m = (pthread_mutex_t *) v->data;
136
137 pthread_mutex_destroy(m);
138 #endif
139
140 v->data = NULL;
141
142 return v;
143 }
144
145
146 /**
147 * mpdm_new_mutex - Creates a new mutex.
148 *
149 * Creates a new mutex.
150 * [Threading]
151 */
mpdm_new_mutex(void)152 mpdm_t mpdm_new_mutex(void)
153 {
154 char *ptr = NULL;
155 int size = 0;
156
157 #ifdef CONFOPT_WIN32
158 HANDLE h;
159
160 h = CreateMutex(NULL, FALSE, NULL);
161
162 if (h != NULL) {
163 size = sizeof(h);
164 ptr = (char *) &h;
165 }
166 #endif
167
168 #ifdef CONFOPT_PTHREADS
169 pthread_mutex_t m;
170
171 if (pthread_mutex_init(&m, NULL) == 0) {
172 size = sizeof(m);
173 ptr = (char *) &m;
174 }
175
176 #endif
177
178 return MPDM_C(MPDM_TYPE_MUTEX, ptr, size);
179 }
180
181
182 /**
183 * mpdm_mutex_lock - Locks a mutex.
184 * @mutex: the mutex to be locked
185 *
186 * Locks a mutex. If the mutex is not already locked,
187 * it waits until it is.
188 * [Threading]
189 */
mpdm_mutex_lock(mpdm_t mutex)190 void mpdm_mutex_lock(mpdm_t mutex)
191 {
192 #ifdef CONFOPT_WIN32
193 HANDLE *h = (HANDLE *) mutex->data;
194
195 WaitForSingleObject(*h, INFINITE);
196 #endif
197
198 #ifdef CONFOPT_PTHREADS
199 pthread_mutex_t *m = (pthread_mutex_t *) mutex->data;
200
201 pthread_mutex_lock(m);
202 #endif
203 }
204
205
206 /**
207 * mpdm_mutex_unlock - Unlocks a mutex.
208 * @mutex: the mutex to be unlocked
209 *
210 * Unlocks a previously locked mutex. The thread
211 * unlocking the mutex must be the one who locked it.
212 * [Threading]
213 */
mpdm_mutex_unlock(mpdm_t mutex)214 void mpdm_mutex_unlock(mpdm_t mutex)
215 {
216 #ifdef CONFOPT_WIN32
217 HANDLE *h = (HANDLE *) mutex->data;
218
219 ReleaseMutex(*h);
220 #endif
221
222 #ifdef CONFOPT_PTHREADS
223 pthread_mutex_t *m = (pthread_mutex_t *) mutex->data;
224
225 pthread_mutex_unlock(m);
226 #endif
227 }
228
229
230 /** semaphores **/
231
vc_semaphore_destroy(mpdm_t v)232 static mpdm_t vc_semaphore_destroy(mpdm_t v)
233 {
234 #ifdef CONFOPT_WIN32
235 HANDLE *h = (HANDLE *) v->data;
236
237 CloseHandle(*h);
238 #endif
239
240 #ifdef CONFOPT_POSIXSEMS
241 sem_t *s = (sem_t *) v->data;
242
243 sem_destroy(s);
244 #endif
245
246 v->data = NULL;
247
248 return v;
249 }
250
251
252 /**
253 * mpdm_new_semaphore - Creates a new semaphore.
254 * @init_value: the initial value of the semaphore.
255 *
256 * Creates a new semaphore with an @init_value.
257 * [Threading]
258 */
mpdm_new_semaphore(int init_value)259 mpdm_t mpdm_new_semaphore(int init_value)
260 {
261 char *ptr = NULL;
262 int size = 0;
263
264 #ifdef CONFOPT_WIN32
265 HANDLE h;
266
267 if ((h = CreateSemaphore(NULL, init_value, 0x7fffffff, NULL)) != NULL) {
268 size = sizeof(h);
269 ptr = (char *) &h;
270 }
271
272 #endif
273
274 #ifdef CONFOPT_POSIXSEMS
275 sem_t s;
276
277 if (sem_init(&s, 0, init_value) == 0) {
278 size = sizeof(s);
279 ptr = (char *) &s;
280 }
281
282 #endif
283
284 return MPDM_C(MPDM_TYPE_SEMAPHORE, ptr, size);
285 }
286
287
288 /**
289 * mpdm_semaphore_wait - Waits for a semaphore to be ready.
290 * @sem: the semaphore to wait onto
291 *
292 * Waits for the value of a semaphore to be > 0. If it's
293 * not, the thread waits until it is.
294 * [Threading]
295 */
mpdm_semaphore_wait(mpdm_t sem)296 void mpdm_semaphore_wait(mpdm_t sem)
297 {
298 #ifdef CONFOPT_WIN32
299 HANDLE *h = (HANDLE *) sem->data;
300
301 WaitForSingleObject(*h, INFINITE);
302 #endif
303
304 #ifdef CONFOPT_POSIXSEMS
305 sem_t *s = (sem_t *) sem->data;
306
307 sem_wait(s);
308 #endif
309 }
310
311
312 /**
313 * mpdm_semaphore_post - Increments the value of a semaphore.
314 * @sem: the semaphore to increment
315 *
316 * Increments by 1 the value of a semaphore.
317 * [Threading]
318 */
mpdm_semaphore_post(mpdm_t sem)319 void mpdm_semaphore_post(mpdm_t sem)
320 {
321 #ifdef CONFOPT_WIN32
322 HANDLE *h = (HANDLE *) sem->data;
323
324 ReleaseSemaphore(*h, 1, NULL);
325 #endif
326
327 #ifdef CONFOPT_POSIXSEMS
328 sem_t *s = (sem_t *) sem->data;
329
330 sem_post(s);
331 #endif
332 }
333
334
335 /** threads **/
336
vc_thread_destroy(mpdm_t v)337 static mpdm_t vc_thread_destroy(mpdm_t v)
338 {
339 v->data = NULL;
340
341 return v;
342 }
343
344
thread_caller(mpdm_t a)345 static void thread_caller(mpdm_t a)
346 {
347 /* ignore return value */
348 mpdm_void(mpdm_exec(mpdm_get_i(a, 0), mpdm_get_i(a, 1), mpdm_get_i(a, 2)));
349
350 /* was referenced in mpdm_exec_thread() */
351 mpdm_unref(a);
352 }
353
354
355 #ifdef CONFOPT_WIN32
win32_thread(LPVOID param)356 DWORD WINAPI win32_thread(LPVOID param)
357 {
358 thread_caller((mpdm_t) param);
359
360 return 0;
361 }
362 #endif
363
364 #ifdef CONFOPT_PTHREADS
pthreads_thread(void * args)365 void *pthreads_thread(void *args)
366 {
367 thread_caller((mpdm_t) args);
368
369 return NULL;
370 }
371 #endif
372
373 /**
374 * mpdm_exec_thread - Runs an executable value in a new thread.
375 * @c: the executable value
376 * @args: executable arguments
377 * @ctxt: the context
378 *
379 * Runs the @c executable value in a new thread. The code
380 * starts executing immediately. The @args and @ctxt arguments
381 * are sent to the executable value as arguments.
382 *
383 * Returns a handle for the thread.
384 * [Threading]
385 */
mpdm_exec_thread(mpdm_t c,mpdm_t args,mpdm_t ctxt)386 mpdm_t mpdm_exec_thread(mpdm_t c, mpdm_t args, mpdm_t ctxt)
387 {
388 mpdm_t a;
389 char *ptr = NULL;
390 int size = 0;
391
392 if (ctxt == NULL)
393 ctxt = MPDM_A(0);
394
395 /* to be unreferenced at thread stop */
396 a = mpdm_ref(MPDM_A(3));
397
398 mpdm_set_i(a, c, 0);
399 mpdm_set_i(a, args, 1);
400 mpdm_set_i(a, ctxt, 2);
401
402 #ifdef CONFOPT_WIN32
403 HANDLE t;
404
405 t = CreateThread(NULL, 0, win32_thread, a, 0, NULL);
406
407 if (t != NULL) {
408 size = sizeof(t);
409 ptr = (char *) &t;
410 }
411
412 #endif
413
414 #ifdef CONFOPT_PTHREADS
415 pthread_t pt;
416
417 if (pthread_create(&pt, NULL, pthreads_thread, a) == 0) {
418 size = sizeof(pthread_t);
419 ptr = (char *) &pt;
420 }
421
422 #endif
423
424 return MPDM_C(MPDM_TYPE_THREAD, ptr, size);
425 }
426
427
428 /** zlib functions **/
429
mpdm_gzip_inflate(unsigned char * cbuf,size_t cz,size_t * dz)430 unsigned char *mpdm_gzip_inflate(unsigned char *cbuf, size_t cz, size_t *dz)
431 {
432 unsigned char *dbuf = NULL;
433
434 #ifdef CONFOPT_ZLIB
435
436 if (cbuf[0] == 0x1f && cbuf[1] == 0x8b) {
437 z_stream d_stream;
438 int err;
439
440 /* size % 2^32 is at the end */
441 *dz = cbuf[cz - 1];
442 *dz = (*dz * 256) + cbuf[cz - 2];
443 *dz = (*dz * 256) + cbuf[cz - 3];
444 *dz = (*dz * 256) + cbuf[cz - 4];
445
446 dbuf = calloc(*dz + 1, 1);
447
448 memset(&d_stream, '\0', sizeof(d_stream));
449
450 d_stream.next_in = cbuf;
451 d_stream.avail_in = cz;
452 d_stream.next_out = dbuf;
453 d_stream.avail_out = *dz;
454
455 if ((err = inflateInit2(&d_stream, 16 + MAX_WBITS)) == Z_OK) {
456 while (err != Z_STREAM_END && err >= 0)
457 err = inflate(&d_stream, Z_FINISH);
458
459 err = inflateEnd(&d_stream);
460 }
461
462 if (err != Z_OK || *dz != d_stream.total_out)
463 dbuf = realloc(dbuf, 0);
464 }
465
466 #endif /* CONFOPT_ZLIB */
467
468 return dbuf;
469 }
470
471
472 /** tar archives **/
473
mpdm_read_tar_mem(const char * fn,const char * tar,const char * tar_e,size_t * z)474 unsigned char *mpdm_read_tar_mem(const char *fn, const char *tar,
475 const char *tar_e, size_t *z)
476 {
477 unsigned char *data = NULL;
478
479 while (!data && tar < tar_e && *tar) {
480 sscanf(&tar[124], "%lo", (long *)z);
481
482 if (strcmp(fn, tar) == 0)
483 data = memcpy(calloc(*z + 1, 1), tar + 512, *z);
484 else
485 tar += (1 + ((*z + 511) / 512)) * 512;
486 }
487
488 return data;
489 }
490
491
mpdm_read_tar_file(const char * fn,FILE * f,size_t * z)492 unsigned char *mpdm_read_tar_file(const char *fn, FILE *f, size_t *z)
493 {
494 unsigned char *data = NULL;
495 char tar[512];
496
497 while (!data && fread(tar, sizeof(tar), 1, f) && *tar) {
498 sscanf(&tar[124], "%lo", (long *)z);
499
500 if (strcmp(fn, tar) == 0) {
501 data = calloc(*z + 1, 1);
502 if (!fread(data, *z, 1, f))
503 data = realloc(data, 0);
504 }
505 else
506 fseek(f, ((*z + 511) / 512) * 512, 1);
507 }
508
509 return data;
510 }
511
512
513 /** zip archives **/
514
515 struct zip_hdr {
516 unsigned int sig;
517 unsigned short min_ver;
518 unsigned short gp_flag;
519 unsigned short comp_meth;
520 unsigned short mtime;
521 unsigned short mdate;
522 unsigned int crc32;
523 unsigned char csz[4];
524 unsigned char usz[4];
525 unsigned char fnsz[2];
526 unsigned char xfsz[2];
527 } __attribute__ ((__packed__));
528
529
530 int puff(unsigned char *dest, /* pointer to destination pointer */
531 unsigned long *destlen, /* amount of output space */
532 const unsigned char *source, /* pointer to source data pointer */
533 unsigned long *sourcelen); /* amount of input available */
534
mpdm_read_zip_mem(const char * fn,const char * zip,const char * zip_e,size_t * z)535 unsigned char *mpdm_read_zip_mem(const char *fn, const char *zip,
536 const char *zip_e, size_t *z)
537 {
538 unsigned char *data = NULL;
539 int sz = strlen(fn);
540
541 while (!data && zip < zip_e) {
542 struct zip_hdr *hdr = (struct zip_hdr *)zip;
543 unsigned char *cdata;
544 unsigned long int usz;
545 unsigned long int csz;
546 unsigned short fnsz;
547 unsigned short xfsz;
548
549 usz = hdr->usz[3] << 24 | hdr->usz[2] << 16 | hdr->usz[1] << 8 | hdr->usz[0];
550 csz = hdr->csz[3] << 24 | hdr->csz[2] << 16 | hdr->csz[1] << 8 | hdr->csz[0];
551 fnsz = hdr->fnsz[1] << 8 | hdr->fnsz[0];
552 xfsz = hdr->xfsz[1] << 8 | hdr->xfsz[0];
553
554 if (zip[0] != 'P' || zip[1] != 'K' || fnsz == 0)
555 break;
556
557 cdata = (unsigned char *)zip + sizeof(struct zip_hdr) + fnsz + xfsz;
558
559 if (sz == fnsz && memcmp(&zip[30], fn, fnsz) == 0) {
560 data = calloc(usz + 1, 1);
561
562 if (hdr->comp_meth == 0)
563 memcpy(cdata, data, usz);
564 else
565 if (hdr->comp_meth == 8)
566 puff(data, &usz, cdata, &csz);
567 else {
568 data = realloc(data, 0);
569 break;
570 }
571
572 *z = usz;
573 }
574 else
575 zip = (char *)cdata + csz;
576 }
577
578 return data;
579 }
580
581
mpdm_read_zip_file(const char * fn,FILE * f,size_t * z)582 unsigned char *mpdm_read_zip_file(const char *fn, FILE *f, size_t *z)
583 {
584 unsigned char *data = NULL;
585
586 return data;
587 }
588
589
590 /** 'generic' archives **/
591
mpdm_read_arch_mem(const char * fn,const char * arch,const char * arch_e,size_t * z)592 unsigned char *mpdm_read_arch_mem(const char *fn, const char *arch,
593 const char *arch_e, size_t *z)
594 {
595 unsigned char *data = NULL;
596
597 if (arch && arch[0] == 'P' && arch[1] == 'K')
598 data = mpdm_read_zip_mem(fn, arch, arch_e, z);
599 else
600 data = mpdm_read_tar_mem(fn, arch, arch_e, z);
601
602 return data;
603 }
604
605
mpdm_read_arch_mem_s(mpdm_t fn,const char * arch,const char * arch_e)606 mpdm_t mpdm_read_arch_mem_s(mpdm_t fn, const char *arch, const char *arch_e)
607 {
608 mpdm_t f, r = NULL;
609 unsigned char *data;
610 size_t z;
611
612 mpdm_ref(fn);
613
614 /* convert to mbs */
615 f = mpdm_ref(MPDM_2MBS(mpdm_string(fn)));
616
617 if ((data = mpdm_read_arch_mem((const char *)f->data, arch, arch_e, &z)) != NULL) {
618 r = MPDM_MBS((char *)data);
619 free(data);
620 }
621
622 mpdm_unref(f);
623 mpdm_unref(fn);
624
625 return r;
626 }
627
628
mpdm_read_arch_file(const char * fn,FILE * f,size_t * z)629 unsigned char *mpdm_read_arch_file(const char *fn, FILE *f, size_t *z)
630 {
631 unsigned char *data = NULL;
632 char sig[2];
633
634 /* read signature */
635 fread(sig, sizeof(sig), 1, f);
636 rewind(f);
637
638 if (sig[0] == 'P' && sig[1] == 'K')
639 data = mpdm_read_zip_file(fn, f, z);
640 else
641 data = mpdm_read_tar_file(fn, f, z);
642
643 return data;
644 }
645
646
mpdm_read_arch_file_s(mpdm_t fn,mpdm_t fd)647 mpdm_t mpdm_read_arch_file_s(mpdm_t fn, mpdm_t fd)
648 {
649 mpdm_t fs, r = NULL;
650 FILE *f = mpdm_get_filehandle(fd);
651
652 mpdm_ref(fn);
653
654 /* convert to mbs */
655 fs = mpdm_ref(MPDM_2MBS(mpdm_string(fn)));
656
657 if (f) {
658 unsigned char *data = NULL;
659 size_t z;
660
661 if ((data = mpdm_read_arch_file((const char *)fs->data, f, &z)) != NULL) {
662 r = MPDM_MBS((char *)data);
663 free(data);
664 }
665 }
666
667 mpdm_unref(fs);
668 mpdm_unref(fn);
669
670 return r;
671 }
672
673
674 /** other utilities **/
675
676 void md5_simple(void *output, const void *input, unsigned long size);
677
mpdm_md5(mpdm_t v)678 mpdm_t mpdm_md5(mpdm_t v)
679 {
680 mpdm_t r = NULL;
681
682 mpdm_ref(v);
683
684 if (mpdm_type(v) == MPDM_TYPE_STRING) {
685 char *ptr;
686 unsigned char md5[16];
687 char md5_s[33];
688 int n;
689
690 ptr = mpdm_wcstombs(mpdm_string(v), &n);
691
692 md5_simple(md5, ptr, n);
693
694 for (n = 0; n < sizeof(md5); n++)
695 sprintf(&md5_s[n * 2], "%02x", md5[n]);
696 md5_s[32] = '\0';
697
698 free(ptr);
699
700 r = MPDM_MBS(md5_s);
701 }
702
703 mpdm_unref(v);
704
705 return r;
706 }
707
708
709 /** data vc **/
710
711 struct mpdm_type_vc mpdm_vc_mutex = { /* VC */
712 L"mutex", /* name */
713 vc_mutex_destroy, /* destroy */
714 vc_default_is_true, /* is_true */
715 vc_default_count, /* count */
716 vc_default_get_i, /* get_i */
717 vc_default_get, /* get */
718 vc_default_string, /* string */
719 vc_default_del_i, /* del_i */
720 vc_default_del, /* del */
721 vc_default_set_i, /* set_i */
722 vc_default_set, /* set */
723 vc_default_exec, /* exec */
724 vc_default_iterator, /* iterator */
725 vc_default_map /* map */
726 };
727
728 struct mpdm_type_vc mpdm_vc_semaphore = { /* VC */
729 L"semaphore", /* name */
730 vc_semaphore_destroy, /* destroy */
731 vc_default_is_true, /* is_true */
732 vc_default_count, /* count */
733 vc_default_get_i, /* get_i */
734 vc_default_get, /* get */
735 vc_default_string, /* string */
736 vc_default_del_i, /* del_i */
737 vc_default_del, /* del */
738 vc_default_set_i, /* set_i */
739 vc_default_set, /* set */
740 vc_default_exec, /* exec */
741 vc_default_iterator, /* iterator */
742 vc_default_map /* map */
743 };
744
745 struct mpdm_type_vc mpdm_vc_thread = { /* VC */
746 L"thread", /* name */
747 vc_thread_destroy, /* destroy */
748 vc_default_is_true, /* is_true */
749 vc_default_count, /* count */
750 vc_default_get_i, /* get_i */
751 vc_default_get, /* get */
752 vc_default_string, /* string */
753 vc_default_del_i, /* del_i */
754 vc_default_del, /* del */
755 vc_default_set_i, /* set_i */
756 vc_default_set, /* set */
757 vc_default_exec, /* exec */
758 vc_default_iterator, /* iterator */
759 vc_default_map /* map */
760 };
761