root/eaccelerator/tags/0.9.4/mm.c

Revision 150, 30.6 kB (checked in by zoeloelip, 3 years ago)

The owner of the a sysvipc mutex will now be changed on creation of

of the mutex. The user will be set to the uid set with
--with-eaccelerator-userid. This way the semaphore doesn't need to
be public writable anymore.

Moved some inactive contributors to the inactive list.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 /*
2    +----------------------------------------------------------------------+
3    | eAccelerator project                                                 |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 2004 - 2005 eAccelerator                               |
6    | http://eaccelerator.net                                              |
7    +----------------------------------------------------------------------+
8    | This program is free software; you can redistribute it and/or        |
9    | modify it under the terms of the GNU General Public License          |
10    | as published by the Free Software Foundation; either version 2       |
11    | of the License, or (at your option) any later version.               |
12    |                                                                      |
13    | This program is distributed in the hope that it will be useful,      |
14    | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
15    | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        |
16    | GNU General Public License for more details.                         |
17    |                                                                      |
18    | You should have received a copy of the GNU General Public License    |
19    | along with this program; if not, write to the Free Software          |
20    | Foundation, Inc., 59 Temple Place - Suite 330, Boston,               |
21    | MA  02111-1307, USA.                                                 |
22    |                                                                      |
23    | A copy is availble at http://www.gnu.org/copyleft/gpl.txt            |
24    +----------------------------------------------------------------------+
25    | Author(s): Dmitry Stogov <dstogov@users.sourceforge.net>             |
26    +----------------------------------------------------------------------+
27    $Id$
28 */
29
30 /* libmm replacement */
31
32 #if !defined(MM_TEST_SHM) && !defined(MM_TEST_SEM)
33 # ifdef HAVE_CONFIG_H
34 #  include "config.h"
35 # endif
36 # include "php.h"
37 #endif
38
39 #ifdef WIN32
40 #  if 1
41 #    define MM_SHM_WIN32
42 #    define MM_SEM_WIN32
43 #  else
44 #    define MM_SHM_MALLOC
45 #    define MM_SEM_NONE
46 #  endif
47 #endif
48
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <errno.h>
52 #include <string.h>
53 #include <stdlib.h>
54 #include <stdio.h>
55 #include <fcntl.h>
56
57 #ifdef WIN32
58 #  include <limits.h>
59 #else
60 #  ifdef HAVE_UNISTD_H
61 #    include <unistd.h>
62 #  endif
63 #  ifdef HAVE_SYS_PARAM_H
64 #    include <sys/param.h>
65 #  endif
66 #  ifdef HAVE_LIMITS_H
67 #    include <limits.h>
68 #  endif
69 #endif
70
71 #if defined(MM_SHM_MMAP_FILE) || defined(MM_SHM_MMAP_ZERO) || defined(MM_SHM_MMAP_ANON) || defined(MM_SHM_MMAP_POSIX) || defined(HAVE_MPROTECT)
72 #  include <sys/mman.h>
73 #endif
74 #if defined(MM_SHM_IPC) || defined(MM_SEM_IPC)
75 #  include <sys/ipc.h>
76 #endif
77 #ifdef MM_SHM_IPC
78 #  include <sys/shm.h>
79 #endif
80 #if defined(MM_SHM_WIN32) || defined(MM_SEM_WIN32)
81 #  include <windows.h>
82 #endif
83 #ifdef MM_SHM_MALLOC
84 #  include <malloc.h>
85 #endif
86 #ifdef MM_SEM_IPC
87 #  include <sys/sem.h>
88 #endif
89 #ifdef MM_SEM_FLOCK
90 #  include <sys/file.h>
91 #endif
92 #ifdef MM_SEM_POSIX
93 #  include <semaphore.h>
94 #endif
95 #ifdef MM_SEM_PTHREAD
96 #  include <pthread.h>
97 #endif
98
99 struct mm_mutex;
100
101 typedef struct mm_free_bucket {
102   size_t                 size;
103   struct mm_free_bucket* next;
104 } mm_free_bucket;
105
106 typedef struct mm_core {
107   size_t           size;
108   void*            start;
109   size_t           available;
110   void*            attach_addr;
111   struct mm_mutex* lock;
112   mm_free_bucket*  free_list;
113 } mm_core;
114
115 typedef union mm_mem_head {
116   size_t size;
117   double a1;
118   int (*a2)(int);
119   void *a3;
120 } mm_mem_head;
121
122 #define MM_SIZE(sz)       (sizeof(mm_mem_head)+(sz))
123 #define PTR_TO_HEAD(p)    (((mm_mem_head *)(p)) - 1)
124 #define HEAD_TO_PTR(p)    ((void *)(((mm_mem_head *)(p)) + 1))
125
126 #define MM mm_core
127 #define MM_PRIVATE
128 #include "mm.h"
129
130 typedef union mm_word {
131   size_t size;
132   void*  ptr;
133   double d;
134   int (*func)(int);
135 } mm_word;
136
137 #if (defined (__GNUC__) && __GNUC__ >= 2)
138 #define MM_PLATFORM_ALIGNMENT (__alignof__ (mm_word))
139 #else
140 #define MM_PLATFORM_ALIGNMENT (sizeof(mm_word))
141 #endif
142
143 #define MM_ALIGN(n) (void*)((((size_t)(n)-1) & ~(MM_PLATFORM_ALIGNMENT-1)) + MM_PLATFORM_ALIGNMENT)
144 /*#define MM_ALIGN(n) (void*)((1+(((size_t)(n)-1) / sizeof(mm_word))) * sizeof(mm_word))*/
145
146 #ifndef MAXPATHLEN
147 #  ifdef PATH_MAX
148 #    define MAXPATHLEN PATH_MAX
149 #  elif defined(_POSIX_PATH_MAX)
150 #    define MAXPATHLEN _POSIX_PATH_MAX
151 #  else
152 #    define MAXPATHLEN 256
153 #  endif
154 #endif
155
156 #undef MM_SHM_CAN_ATTACH
157
158 static int strxcat(char* dst, const char* src, int size) {
159   int dst_len = strlen(dst);
160   int src_len = strlen(src);
161   if (dst_len + src_len < size) {
162     memcpy(dst+dst_len, src, src_len+1);
163     return 1;
164   } else {
165     memcpy(dst+dst_len, src, (size-1)-dst_len);
166     dst[size-1] = '\000';
167     return 0;
168   }
169 }
170
171 #if defined(MM_SEM_SPINLOCK)
172
173 #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
174 #  include "x86_spinlocks.h"
175 #else
176 #  error "spinlocks are not implemented for your system"
177 #endif
178
179 /*********************************/
180 /* Semaphores                    */
181 /********************************/
182
183 /* ######################################################################### */
184
185 #define MM_SEM_TYPE "spinlock"
186 #define MM_SEM_CAN_ATTACH
187
188 typedef struct mm_mutex {
189   spinlock_t spinlock;
190 } mm_mutex;
191
192 static int mm_attach_lock(const char* key, mm_mutex* lock) {
193   return 1;
194 }
195
196 static int mm_init_lock(const char* key, mm_mutex* lock) {
197   spinlock_init(&lock->spinlock);
198   return 1;
199 }
200
201 static int mm_do_lock(mm_mutex* lock, int kind) {
202   spinlock_lock(&lock->spinlock);
203   return 1;
204 }
205
206 static int mm_do_unlock(mm_mutex* lock) {
207   spinlock_unlock(&lock->spinlock);
208   return 1;
209 }
210
211 static void mm_destroy_lock(mm_mutex* lock) {
212 }
213
214 /* ######################################################################### */
215
216 #elif defined(MM_SEM_PTHREAD)
217
218 #define MM_SEM_TYPE "pthread"
219
220 typedef struct mm_mutex {
221   pthread_mutex_t mutex;
222 } mm_mutex;
223
224 static int mm_init_lock(const char* key, mm_mutex* lock) {
225   pthread_mutexattr_t mattr;
226
227   if (pthread_mutexattr_init(&mattr) != 0) {
228     return 0;
229   }
230   if (pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED) != 0) {
231     return 0;
232   }
233   if (pthread_mutex_init(&lock->mutex, &mattr) != 0) {
234     return 0;
235   }
236   pthread_mutexattr_destroy(&mattr);
237   return 1;
238 }
239
240 static int mm_do_lock(mm_mutex* lock, int kind) {
241   if (pthread_mutex_lock(&lock->mutex) != 0) {
242     return 0;
243   }
244   return 1;
245 }
246
247 static int mm_do_unlock(mm_mutex* lock) {
248   if (pthread_mutex_unlock(&lock->mutex) != 0) {
249     return 0;
250   }
251   return 1;
252 }
253
254 static void mm_destroy_lock(mm_mutex* lock) {
255   pthread_mutex_destroy(&lock->mutex);
256 }
257
258 /* ######################################################################### */
259
260 #elif defined(MM_SEM_POSIX)
261
262 /* not tested */
263
264 #define MM_SEM_TYPE "posix"
265
266 typedef struct mm_mutex {
267   sem_t* sem;
268 } mm_mutex;
269
270 static int mm_init_lock(const char* key, mm_mutex* lock) {
271 #ifdef SEM_NAME_LEN
272   char s[SEM_NAME_LEN];
273
274   strncpy(s,key,SEM_NAME_LEN-1);
275   strxcat(s,".sem.XXXXXX",SEM_NAME_LEN);
276 #else
277   char s[MAXPATHLEN];
278
279   strncpy(s,key,MAXPATHLEN-1);
280   strxcat(s,".sem.XXXXXX",MAXPATHLEN);
281 #endif
282   if (mktemp(s) == NULL) return 0;
283   if ((lock->sem = sem_open(s, O_CREAT, S_IRUSR | S_IWUSR, 1)) == (sem_t*)SEM_FAILED) {
284     return 0;
285   }
286   sem_unlink(s);
287   return 1;
288 }
289
290 static int mm_do_lock(mm_mutex* lock, int kind) {
291   return (sem_wait(lock->sem) == 0);
292 }
293
294 static int mm_do_unlock(mm_mutex* lock) {
295   return (sem_post(lock->sem) == 0);
296 }
297
298 static void mm_destroy_lock(mm_mutex* lock) {
299   sem_close(lock->sem);
300 }
301
302 /* ######################################################################### */
303
304 #elif defined(MM_SEM_IPC)
305
306 #define MM_SEM_TYPE "sysvipc"
307
308 #ifndef HAVE_UNION_SEMUN
309 union semun {
310     int val;
311     struct semid_ds *buf;
312     unsigned short *array;
313     struct seminfo *__buf;
314 };
315 #endif
316
317 typedef struct mm_mutex {
318   int semid;
319 } mm_mutex;
320
321 static int mm_init_lock(const char* key, mm_mutex* lock) {
322   int rc;
323   union semun arg;
324   struct semid_ds buf;
325
326   if ((lock->semid = semget(IPC_PRIVATE, 1, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR)) < 0) {
327     return 0;
328   }
329
330   arg.buf = &buf;
331   do {
332     rc = semctl(lock->semid, 0, IPC_STAT, arg);
333   } while (rc < 0 && errno == EINTR);
334
335   buf.sem_perm.uid = EA_USERID;
336
337   do {
338     rc = semctl(lock->semid, 0, IPC_SET, arg);
339   } while (rc < 0 && errno == EINTR);
340  
341   arg.val = 1;
342   do {
343     rc = semctl(lock->semid, 0, SETVAL, arg);
344   } while (rc < 0 && errno == EINTR);
345   if (rc < 0) {
346     do {
347       semctl(lock->semid, 0, IPC_RMID, 0);
348     } while (rc < 0 && errno == EINTR);
349     return 0;
350   }
351   return 1;
352 }
353
354 static int mm_do_lock(mm_mutex* lock, int kind) {
355   int rc;
356   struct sembuf op;
357
358   op.sem_num = 0;
359   op.sem_op  = -1;
360   op.sem_flg = SEM_UNDO;
361   do {
362     rc = semop(lock->semid, &op, 1);
363   } while (rc < 0 && errno == EINTR);
364   return (rc == 0);
365 }
366
367 static int mm_do_unlock(mm_mutex* lock) {
368   int rc;
369   struct sembuf op;
370
371   op.sem_num = 0;
372   op.sem_op  = 1;
373   op.sem_flg = SEM_UNDO;
374   do {
375     rc = semop(lock->semid, &op, 1);
376   } while (rc < 0 && errno == EINTR);
377   return (rc == 0);
378 }
379
380 static void mm_destroy_lock(mm_mutex* lock) {
381   int rc;
382   do {
383     rc = semctl(lock->semid, 0, IPC_RMID, 0);
384   } while (rc < 0 && errno == EINTR);
385 }
386
387 #elif defined(MM_SEM_FCNTL)
388
389 #define MM_SEM_TYPE "fcntl"
390
391 typedef struct mm_mutex {
392   int fd;
393 } mm_mutex;
394
395 static int mm_init_lock(const char* key, mm_mutex* lock) {
396   char s[MAXPATHLEN];
397
398   strncpy(s,key,MAXPATHLEN-1);
399   strxcat(s,".sem.XXXXXX",MAXPATHLEN);
400   lock->fd =mkstemp(s);
401   if (lock->fd != -1) {
402     unlink(s);
403   }
404   return (lock->fd != -1);
405 }
406
407 static int mm_do_lock(mm_mutex* lock, int kind) {
408   int rc;
409   struct flock l;
410   l.l_whence   = SEEK_SET;
411   l.l_start    = 0;
412   l.l_len      = 0;
413   l.l_pid      = 0;
414   if (kind == MM_LOCK_RD) {
415     l.l_type     = F_RDLCK;
416   } else {
417     l.l_type     = F_WRLCK;
418   }
419   do {
420     rc = fcntl(lock->fd, F_SETLKW, &l);
421   } while (rc < 0 && errno == EINTR);
422   return (rc == 0);
423 }
424
425 static int mm_do_unlock(mm_mutex* lock) {
426   int rc;
427   struct flock l;
428   l.l_whence   = SEEK_SET;
429   l.l_start    = 0;
430   l.l_len      = 0;
431   l.l_pid      = 0;
432   l.l_type     = F_UNLCK;
433   do {
434     rc = fcntl(lock->fd, F_SETLKW, &l);
435   } while (rc < 0 && errno == EINTR);
436   return (rc == 0);
437 }
438
439 static void mm_destroy_lock(mm_mutex* lock) {
440   close(lock->fd);
441 }
442
443 /* ######################################################################### */
444
445 #elif defined(MM_SEM_FLOCK)
446
447 /* this method is not thread safe */
448
449 #define MM_SEM_TYPE "flock"
450
451 static int   mm_flock_fd  = -1;
452 static pid_t mm_flock_pid = -1;
453
454 typedef struct mm_mutex {
455   char filename[MAXPATHLEN];
456 } mm_mutex;
457
458 static int mm_init_lock(const char* key, mm_mutex* lock) {
459   strncpy(lock->filename,key,MAXPATHLEN-1);
460   strxcat(lock->filename,".sem.XXXXXX",MAXPATHLEN);
461   mm_flock_fd =mkstemp(lock->filename);
462   if (mm_flock_fd != -1) {
463 #if defined(F_SETFD) && defined(FD_CLOEXEC)
464     fcntl(mm_flock_fd, F_SETFD, FD_CLOEXEC);
465 #endif
466     mm_flock_pid = getpid();
467     return 1;
468   }
469   return 0;
470 }
471
472 static int mm_do_lock(mm_mutex* lock, int kind) {
473   pid_t pid = getpid();
474   int rc;
475   if (kind == MM_LOCK_RD) {
476     kind = LOCK_SH;
477   } else {
478     kind = LOCK_EX;
479   }
480
481   if (mm_flock_fd == -1 || mm_flock_pid != pid) {
482     mm_flock_fd = open(lock->filename, O_RDWR, S_IRUSR | S_IWUSR);
483     if (mm_flock_fd == -1) {
484       return 0;
485     }
486 #if defined(F_SETFD) && defined(FD_CLOEXEC)
487     fcntl(mm_flock_fd, F_SETFD, FD_CLOEXEC);
488 #endif
489     mm_flock_pid = pid;
490   }
491   do {
492     rc = flock(mm_flock_fd, kind);
493   } while (rc < 0 && errno == EINTR);
494   return (rc == 0);
495 }
496
497 static int mm_do_unlock(mm_mutex* lock) {
498   int rc;
499   if (mm_flock_fd == -1) {
500     mm_flock_fd = open(lock->filename, O_RDWR, S_IRUSR | S_IWUSR);
501     if (mm_flock_fd == -1) {
502       return 0;
503     }
504   }
505   do {
506     rc = flock(mm_flock_fd, LOCK_UN);
507   } while (rc < 0 && errno == EINTR);
508   return (rc == 0);
509 }
510
511 static void mm_destroy_lock(mm_mutex* lock) {
512   close(mm_flock_fd);
513   unlink(lock->filename);
514 }
515
516 /* ######################################################################### */
517
518 #elif defined(MM_SEM_BEOS)
519
520 #define MM_SEM_TYPE "beos"
521 #error "Semophore type (MM_SEM_BEOS) is not implemented"
522
523 #elif defined(MM_SEM_OS2)
524
525 #define MM_SEM_TYPE "os2"
526 #error "Semophore type (MM_SEM_OS2) is not implemented"
527
528 #elif defined(MM_SEM_WIN32)
529
530 #define MM_SEM_TYPE "win32"
531 #define MM_SEM_CAN_ATTACH
532
533 typedef struct mm_mutex {
534   HANDLE hMutex;
535 } mm_mutex;
536
537 static mm_mutex g_lock;
538
539 static int mm_attach_lock(const char* key, mm_mutex* lock) {
540   char* ch;
541   char name[256];
542   HANDLE hMutex;
543
544   strncpy(name, key, 255);
545   strxcat(name, ".sem", 255);
546   for (ch = name; *ch; ++ch) {
547     if (*ch == ':' || *ch == '/' || *ch == '\\') {
548       *ch = '_';
549     }
550   }
551   g_lock.hMutex = hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, name);
552   if (!g_lock.hMutex) {
553     return 0;
554   }
555   return 1;
556 }
557
558 static int mm_init_lock(const char* key, mm_mutex* lock) {
559   char* ch;
560   char name[256];
561   strncpy(name, key, 255);
562   strxcat(name, ".sem", 255);
563   for (ch = name; *ch; ++ch) {
564     if (*ch == ':' || *ch == '/' || *ch == '\\') {
565       *ch = '_';
566     }
567   }
568   g_lock.hMutex = CreateMutex(NULL, FALSE, name);
569   if (!g_lock.hMutex) {
570     return 0;
571   }
572   return 1;
573 }
574
575 static void mm_destroy_lock(mm_mutex* lock) {
576   CloseHandle(g_lock.hMutex);
577 }
578
579 static int mm_do_lock(mm_mutex* lock, int kind) {
580   DWORD rv;
581
582   rv = WaitForSingleObject(g_lock.hMutex, INFINITE);
583
584   if (rv == WAIT_OBJECT_0 || rv == WAIT_ABANDONED) {
585     return 1;
586   }
587   return 0;
588 }
589
590 static int mm_do_unlock(mm_mutex* lock) {
591   if (ReleaseMutex(g_lock.hMutex) == 0) {
592     return 0;
593   }
594   return 1;
595 }
596
597 /* ######################################################################### */
598
599 #elif defined(MM_SEM_NONE)
600
601 #define MM_SEM_TYPE "none"
602 #define MM_SEM_CAN_ATTACH
603
604
605 typedef struct mm_mutex {
606   int semid;
607 } mm_mutex;
608
609 static int mm_attach_lock(const char* key, mm_mutex* lock) {
610   return 1;
611 }
612
613 static int mm_init_lock(const char* key, mm_mutex* lock) {
614   return 1;
615 }
616
617 static void mm_destroy_lock(mm_mutex* lock) {
618 }
619
620 static int mm_do_lock(mm_mutex* lock, int kind) {
621   return 1;
622 }
623
624 static int mm_do_unlock(mm_mutex* lock) {
625   return 1;
626 }
627
628 #else
629 #  error "Semaohore type is not selected. Define one of the following: MM_SEM_SPINLOCK, MM_SEM_PTHREAD, MM_SEM_POSIX, MM_SEM_IPC, MM_SEM_FCNTL, MM_SEM_FLOCK, MM_SEM_BEOS, MM_SEM_OS2, MM_SEM_WIN32"
630 #endif
631
632 int mm_lock(MM* mm, int kind) {
633   if (mm_do_lock(mm->lock, kind)) {
634     return 1;
635   } else {
636 #if !defined(MM_TEST_SEM) && !defined(MM_TEST_SHM)
637     ea_debug_error("eAccelerator: Could not lock!\n");
638 #endif
639     return 0;
640   }
641 }
642
643 int mm_unlock(MM* mm) {
644   if (mm_do_unlock(mm->lock)) {
645     return 1;
646   } else {
647 #if !defined(MM_TEST_SEM) && !defined(MM_TEST_SHM)
648     ea_debug_error("eAccelerator: Could not release lock!\n");
649 #endif
650     return 0;
651   }
652 }
653
654 /* Shared Memory Implementations */
655
656 /* ######################################################################### */
657
658 #if defined(MM_SHM_IPC)
659
660 #define MM_SHM_TYPE "sysvipc"
661
662 #ifndef SHM_R
663 # define SHM_R 0444 /* read permission */
664 #endif
665 #ifndef SHM_W
666 # define SHM_W 0222 /* write permission */
667 #endif
668
669 static MM* mm_create_shm(const char* key, size_t size) {
670   int fd;
671   void** segment = NULL;
672   if ((fd = shmget(IPC_PRIVATE, size, (IPC_CREAT | SHM_R | SHM_W))) != -1) {
673     MM* p;
674     if ((p = (MM*)shmat(fd, NULL, 0)) != ((void *)-1)) {
675       struct shmid_ds shmbuf;
676       if (shmctl(fd, IPC_STAT, &shmbuf) == 0) {
677         shmbuf.shm_perm.uid = getuid();
678         shmbuf.shm_perm.gid = getgid();
679         if (shmctl(fd, IPC_SET, &shmbuf) == 0) {
680           shmctl(fd, IPC_RMID, NULL);
681           p->size = size;
682           segment = (void**)((char*)p+sizeof(MM));
683           *segment = (void*)-1;
684           segment++;
685           p->start = segment;
686           return p;
687         }
688       }
689       shmdt(p);
690     }
691     shmctl(fd, IPC_RMID, NULL);
692   } else {
693     /* can't get one shared memory segment, trying to get several */
694     size_t seg_size = 1024*1024;
695     size_t orig_size = size;
696     void*  p;
697     MM*    root = NULL;
698     char*  prev = NULL;
699
700     while (seg_size <= size/2) {
701       seg_size *= 2;
702     }
703     while ((fd = shmget(IPC_PRIVATE, seg_size, (IPC_CREAT | SHM_R | SHM_W))) == -1) {
704       if (seg_size <= 1024*1024) {
705         return (MM*)-1;
706       }
707       seg_size /= 2;
708     }
709     while (size > 0) {
710       if (fd != -1 ||
711           (fd = shmget(IPC_PRIVATE, (size > seg_size)?seg_size:size, (IPC_CREAT | SHM_R | SHM_W))) != -1) {
712         if ((p = (void *)shmat(fd, prev?(prev+seg_size):NULL, 0)) != ((void *)-1) &&
713             (prev == NULL || prev + seg_size == p)) {
714           struct shmid_ds shmbuf;
715 /*???
716           memset(p, 0, (size > seg_size)?seg_size:size);
717 */
718           if (shmctl(fd, IPC_STAT, &shmbuf) == 0) {
719             shmbuf.shm_perm.uid = getuid();
720             shmbuf.shm_perm.gid = getgid();
721             if (shmctl(fd, IPC_SET, &shmbuf) == 0) {
722               shmctl(fd, IPC_RMID, NULL);
723               if (root == NULL) {
724                 root = (MM*)p;
725                 segment = (void**)((char*)p+sizeof(MM));
726               } else {
727                 *segment = p;
728                 segment++;
729               }
730               prev = (char*)p;
731               fd = -1;
732               if (size > seg_size) {
733                 size -= seg_size;
734               } else {
735                 size = 0;
736               }
737               continue;
738             }
739           }
740           shmdt(p);
741         }
742         shmctl(fd, IPC_RMID, NULL);
743       }
744       if (root != NULL) {
745         while (segment > (void**)((char*)root+sizeof(MM))) {
746           segment--;
747           shmdt(*segment);
748         }
749       }
750       shmdt(root);
751       return (MM*)-1;
752     }
753     *segment = (void*)-1;
754     segment++;
755     root->size  = orig_size;
756     root->start = (void*)segment;
757     return root;
758   }
759   return (MM*)-1;
760 }
761
762 static void mm_destroy_shm(MM* mm) {
763   void** segment = (void**)((char*)mm+sizeof(MM));
764   while (*segment != (void*)-1) {
765     shmdt(*segment);
766     ++segment;
767   }
768   shmdt(mm);
769 }
770
771 /* ######################################################################### */
772
773 #elif defined(MM_SHM_MMAP_ANON)
774
775 #define MM_SHM_TYPE "mmap_anon"
776
777 #ifndef MAP_ANON
778 #  ifdef MAP_ANONYMOUS
779 #    define MAP_ANON MAP_ANONYMOUS
780 #  endif
781 #endif
782
783 static MM* mm_create_shm(const char* key, size_t size) {
784   MM* p;
785   p = (MM*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
786   if (p != (MM*)-1) {
787     p->size = size;
788     p->start = (char*)p+sizeof(MM);
789   }
790   return p;
791 }
792
793 static void mm_destroy_shm(MM* mm) {
794   munmap(mm,mm->size);
795 }
796
797 /* ######################################################################### */
798
799 #elif defined(MM_SHM_MMAP_ZERO)
800
801 #define MM_SHM_TYPE "mmap_zero"
802
803 static MM* mm_create_shm(const char* key, size_t size) {
804   MM* p;
805   int fd = open("/dev/zero", O_RDWR, S_IRUSR | S_IWUSR);
806   if (fd == -1) {
807     return (MM*)-1;
808   }
809   p = (MM*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
810   close(fd);
811   if (p != (MM*)-1) {
812     p->size = size;
813     p->start = (char*)p+sizeof(MM);
814   }
815   return p;
816 }
817
818 static void mm_destroy_shm(MM* mm) {
819   munmap(mm,mm->size);
820 }
821
822 /* ######################################################################### */
823
824 #elif defined(MM_SHM_MMAP_POSIX)
825
826 #define MM_SHM_TYPE "mmap_posix"
827
828 /* Not Tested */
829
830 static MM* mm_create_shm(const char* key, size_t size) {
831   MM* p;
832   int fd;
833   char s[MAXPATHLEN];
834
835   strncpy(s,key,MAXPATHLEN-1);
836   strxcat(s,".shm.XXXXXX",MAXPATHLEN);
837   if (mktemp(s) == NULL) {
838     return (MM*)-1;
839   }
840   if ((fd = shm_open(s, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) == -1) {
841     return (MM*)-1;
842   }
843   if (ftruncate(fd, size) < 0) {
844     close(fd);
845     shm_unlink(s);
846     return (MM*)-1;
847   }
848   p = (MM*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
849   shm_unlink(s);
850   close(fd);
851   if (p != (MM*)-1) {
852     p->size = size;
853     p->start = (char*)p+sizeof(MM);
854   }
855   return p;
856 }
857
858 static void mm_destroy_shm(MM* mm) {
859   munmap(mm,mm->size);
860 }
861
862 /* ######################################################################### */
863
864 #elif defined(MM_SHM_MMAP_FILE)
865
866 #define MM_SHM_TYPE "mmap_file"
867
868 static MM* mm_create_shm(const char* key, size_t size) {
869   MM* p;
870   int fd;
871   char s[MAXPATHLEN];
872
873   strncpy(s,key,MAXPATHLEN-1);
874   strxcat(s,".shm.XXXXXX",MAXPATHLEN);
875   fd = mkstemp(s);
876   if (fd < 0) {
877     return (MM*)-1;
878   }
879   if (ftruncate(fd, size) < 0) {
880     return (MM*)-1;
881   }
882   p = (MM*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
883   close(fd);
884   unlink(s);
885   if (p != (MM*)-1) {
886     p->size = size;
887     p->start = (char*)p+sizeof(MM);
888   }
889   return p;
890 }
891
892 static void mm_destroy_shm(MM* mm) {
893   munmap(mm,mm->size);
894 }
895
896 /* ######################################################################### */
897
898 #elif defined(MM_SHM_BEOS)
899
900 #define MM_SHM_TYPE "beos"
901 #error "Shared memeory type (MM_SHM_BEOS) is not implemented"
902
903 /* ######################################################################### */
904
905 #elif defined(MM_SHM_OS2)
906
907 #define MM_SHM_TYPE "os2"
908 #error "Shared memeory type (MM_SHM_OS2) is not implemented"
909
910 /* ######################################################################### */
911
912 #elif defined(MM_SHM_WIN32)
913
914 #define MM_SHM_TYPE "win32"
915 #define MM_SHM_CAN_ATTACH
916
917 static MM* mm_attach_shm(const char* key, size_t size) {
918   HANDLE  shm_handle;
919   MM*     mm;
920   MM*     addr;
921   MM**    addr_ptr;
922   char    s[MAXPATHLEN];
923   char*   ch;
924
925
926   strcpy(s,key);
927   for (ch = s; *ch; ++ch) {
928     if (*ch == ':' || *ch == '/' || *ch == '\\') {
929       *ch = '_';
930     }
931   }
932
933   shm_handle = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, s);
934   if (shm_handle) {
935     mm = (MM*)MapViewOfFile(shm_handle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
936     if (mm == NULL) {
937       return (MM*)-1;
938     }
939 /*
940     if (mm->size != size) {
941       UnmapViewOfFile(mm);
942       CloseHandle(shm_handle);
943       return (MM*)-1;
944     }
945 */
946     addr_ptr = (MM**)(((char*)mm)+sizeof(MM));
947     addr = *addr_ptr;
948     if (addr != mm) {
949       UnmapViewOfFile(mm);
950       mm = (MM*)MapViewOfFileEx(shm_handle, FILE_MAP_ALL_ACCESS, 0, 0, 0, addr);
951       if (mm == NULL) {
952         return (MM*)-1;
953       }
954     }
955 /*  CloseHandle(shm_handle);*/
956     return mm;
957   }
958   return (MM*)-1;
959 }
960
961 static MM* mm_create_shm(const char* key, size_t size) {
962   HANDLE  shm_handle;
963   MM*     mm;
964   MM**    addr_ptr;
965   char    s[MAXPATHLEN];
966   char*   ch;
967
968
969   strcpy(s,key);
970   for (ch = s; *ch; ++ch) {
971     if (*ch == ':' || *ch == '/' || *ch == '\\') {
972       *ch = '_';
973     }
974   }
975
976   shm_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, s);
977   if (!shm_handle) {
978     return (MM*)-1;
979   }
980   mm = (MM*)MapViewOfFileEx(shm_handle, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
981   if (mm == NULL) {
982     return (MM*)-1;
983   }
984   addr_ptr = (MM**)(((char*)mm)+sizeof(MM));
985   *addr_ptr = mm;
986   mm->size = size;
987   mm->start = ((char*)mm)+sizeof(MM)+sizeof(void*);
988
989 /*  CloseHandle(shm_handle);*/
990   return mm;
991 }
992
993 static void mm_destroy_shm(MM* mm) {
994   UnmapViewOfFile(mm);
995 }
996
997 /* ######################################################################### */
998
999 #elif defined(MM_SHM_MALLOC)
1000
1001 #define MM_SHM_TYPE "malloc"
1002
1003 static void* mm_create_shm(const char* key, size_t size) {
1004   MM* p = (MM*)malloc(sizeof(MM));
1005   if (p == NULL) {
1006     return (MM*)-1;
1007   }
1008   p->size  = size;
1009   p->start = NULL;
1010   return p;
1011 }
1012
1013 static void mm_destroy_shm(MM* mm) {
1014   free(mm);
1015 }
1016
1017 /* ######################################################################### */
1018
1019 #else
1020 #define MM_SHM_TYPE "none"
1021 #  error "Shared memeory type is not selected. Define one of the following: MM_SHM_IPC, MM_SHM_MMAP_ANON, MM_SHM_MMAP_ZERO, MM_SHM_MMAP_FILE, MM_SHM_MALLOC, MM_SHM_BEOS, MM_SHM_OS2, MM_SHM_WIN32"
1022 #endif
1023
1024 #ifdef MM_SHM_MALLOC
1025 static void mm_init(MM* mm) {
1026   mm->available = mm->size - sizeof(MM);
1027   mm->lock = malloc(sizeof(mm_mutex));
1028 }
1029
1030 void* mm_malloc_nolock(MM* mm, size_t size) {
1031   if (size > 0) {
1032     mm_mem_head *p = NULL;
1033     if (mm->available >= MM_SIZE(size)) {
1034       p = malloc(MM_SIZE(size));
1035       if (p != NULL) {
1036         p->size = MM_SIZE(size);
1037         mm->available -= MM_SIZE(size);
1038       }
1039     }
1040     if (p != NULL) {
1041       return HEAD_TO_PTR(p);
1042     }
1043   }
1044   return NULL;
1045 }
1046
1047 void mm_free_nolock(MM* mm, void* x) {
1048   if (x != NULL) {
1049     mm_mem_head *p;
1050     p = PTR_TO_HEAD(x);
1051     mm->available += p->size;
1052     free(p);
1053   }
1054 }
1055
1056 size_t mm_maxsize(MM* mm) {
1057   size_t ret;
1058   if (!mm_lock(mm, MM_LOCK_RD)) {
1059     return 0;
1060   }
1061   ret = mm->available - MM_SIZE(0);
1062   mm_unlock(mm);
1063   return ret;
1064 }
1065
1066 #else
1067 static void mm_init(MM* mm) {
1068   mm->start = MM_ALIGN(mm->start);
1069   mm->attach_addr = (void*)mm;
1070   mm->lock = mm->start;
1071   mm->start = MM_ALIGN((void*)(((char*)(mm->start)) + sizeof(mm_mutex)));
1072   mm->available = mm->size - (((char*)(mm->start))-(char*)mm);
1073   mm->free_list = (mm_free_bucket*)mm->start;
1074   mm->free_list->size = mm->available;
1075   mm->free_list->next = NULL;
1076 }
1077
1078 void* mm_malloc_nolock(MM* mm, size_t size) {
1079   if (size > 0) {
1080     mm_mem_head* x = NULL;
1081     size_t realsize = (size_t)MM_ALIGN(MM_SIZE(size));
1082     if (realsize <= mm->available) {
1083       /* Search for free bucket */
1084       mm_free_bucket* p = mm->free_list;
1085       mm_free_bucket* q = NULL;
1086       mm_free_bucket* best = NULL;
1087       mm_free_bucket* best_prev = NULL;
1088       while (p != NULL) {
1089         if (p->size == realsize) {
1090           /* Found free bucket with the same size */
1091           if (q == NULL) {
1092             mm->free_list = p->next;
1093             x = (mm_mem_head*)p;
1094           } else {
1095             q->next = p->next;
1096             x = (mm_mem_head*)p;
1097           }
1098           break;
1099         } else if (p->size > realsize && (best == NULL || best->size > p->size)) {
1100           /* Found best bucket (smallest bucket with the grater size) */
1101           best = p;
1102           best_prev = q;
1103         }
1104         q = p;
1105         p = p->next;
1106       }
1107       if (x == NULL && best != NULL) {
1108         if (best->size-realsize < sizeof(mm_free_bucket)) {
1109           realsize = best->size;
1110           x = (mm_mem_head*)best;
1111           if (best_prev == NULL) {
1112             mm->free_list = best->next;
1113           } else {
1114             best_prev->next = best->next;
1115           }
1116         } else {
1117           if (best_prev == NULL) {
1118             mm->free_list = (mm_free_bucket*)((char*)best + realsize);
1119             mm->free_list->size = best->size-realsize;
1120             mm->free_list->next = best->next;
1121           } else {
1122             best_prev->next = (mm_free_bucket*)((char*)best + realsize);
1123             best_prev->next->size = best->size-realsize;
1124             best_prev->next->next = best->next;
1125           }
1126           best->size = realsize;
1127           x = (mm_mem_head*)best;
1128         }
1129       }
1130       if (x != NULL) {
1131         mm->available -= realsize;
1132       }
1133     }
1134     if (x != NULL) {
1135       return HEAD_TO_PTR(x);
1136     }
1137   }
1138   return NULL;
1139 }
1140
1141 void mm_free_nolock(MM* mm, void* x) {
1142   if (x != NULL) {
1143     if (x >= mm->start && x < (void*)((char*)mm + mm->size)) {
1144       mm_mem_head *p = PTR_TO_HEAD(x);
1145       size_t size = p->size;
1146       if ((char*)p+size <= (char*)mm + mm->size) {
1147         mm_free_bucket* b = (mm_free_bucket*)p;
1148         b->next = NULL;
1149         if (mm->free_list == NULL) {
1150           mm->free_list = b;
1151         } else {
1152           mm_free_bucket* q = mm->free_list;
1153           mm_free_bucket* prev = NULL;
1154           mm_free_bucket* next = NULL;
1155           while (q != NULL) {
1156             if (b < q) {
1157               next = q;
1158               break;
1159             }
1160             prev = q;
1161             q = q->next;
1162           }
1163           if (prev != NULL && (char*)prev+prev->size == (char*)b) {
1164             if ((char*)next == (char*)b+size) {
1165               /* merging with prev and next */
1166               prev->size += size + next->size;
1167               prev->next = next->next;
1168             } else {
1169               /* merging with prev */
1170               prev->size += size;
1171             }
1172           } else {
1173             if ((char*)next == (char*)b+size) {
1174               /* merging with next */
1175               b->size += next->size;
1176               b->next = next->next;
1177             } else {
1178               /* don't merge */
1179               b->next = next;
1180             }
1181             if (prev != NULL) {
1182               prev->next = b;
1183             } else {
1184               mm->free_list = b;
1185             }
1186           }
1187         }
1188         mm->available += size;
1189       }
1190     }
1191   }
1192 }
1193
1194 size_t mm_maxsize(MM* mm) {
1195   size_t ret = MM_SIZE(0);
1196   mm_free_bucket* p;
1197   if (!mm_lock(mm, MM_LOCK_RD)) {
1198     return 0;
1199   }
1200   p = mm->free_list;
1201   while (p != NULL) {
1202     if (p->size > ret) {
1203       ret = p->size;
1204     }
1205     p = p->next;
1206   }
1207   mm_unlock(mm);
1208   return ret - MM_SIZE(0);
1209 }
1210 #endif
1211
1212 void* mm_malloc_lock(MM* mm, size_t size) {
1213   void *ret;
1214   if (!mm_lock(mm, MM_LOCK_RW)) {
1215     return NULL;
1216   }
1217   ret = mm_malloc_nolock(mm,size);
1218   mm_unlock(mm);
1219   return ret;
1220 }
1221
1222 void mm_free_lock(MM* mm, void* x) {
1223   mm_lock(mm, MM_LOCK_RW);
1224   mm_free_nolock(mm,x);
1225   mm_unlock(mm);
1226 }
1227
1228 void mm_set_attach(MM* mm, void* attach_addr) {
1229   mm->attach_addr = attach_addr;
1230 }
1231
1232 void* mm_attach(size_t size, const char* key) {
1233 #ifdef MM_SHM_CAN_ATTACH
1234   MM* mm = mm_attach_shm(key, size);
1235   if (mm == (MM*)-1) {
1236     return NULL;
1237   }
1238 #ifdef MM_SEM_CAN_ATTACH
1239   if (!mm_attach_lock(key, mm->lock)) {
1240     mm_destroy_shm(mm);
1241     return NULL;
1242   }
1243 #endif
1244   return mm->attach_addr;
1245 #else
1246   return NULL;
1247 #endif
1248 }
1249
1250 MM* mm_create(size_t size, const char* key) {
1251   MM* p;
1252   if (size == 0) {
1253     size = 32 * 1024 * 1024;
1254   }
1255   p = mm_create_shm(key, size);
1256   if (p == (MM*)-1) {
1257     return NULL;
1258   }
1259   mm_init(p);
1260   if (p->lock == NULL) {
1261     mm_destroy_shm(p);
1262     return NULL;
1263   }
1264   if (!mm_init_lock(key, p->lock)) {
1265     mm_destroy_shm(p);
1266     return NULL;
1267   }
1268   return p;
1269 }
1270
1271 void mm_destroy(MM* mm) {
1272   if (mm != NULL) {
1273     mm_destroy_lock(mm->lock);
1274     mm_destroy_shm(mm);
1275   }
1276 }
1277
1278 size_t mm_size(MM* mm) {
1279   if (mm != NULL) {
1280     return mm->size;
1281   }
1282   return 0;
1283 }
1284
1285 size_t mm_sizeof(MM* mm, void* x) {
1286   mm_mem_head *p;
1287   size_t ret;
1288   if (mm == NULL || x == NULL || !mm_lock(mm, MM_LOCK_RD)) {
1289     return 0;
1290   }
1291   p = PTR_TO_HEAD(x);
1292   ret = p->size;
1293   mm_unlock(mm);
1294   return ret;
1295 }
1296
1297 size_t mm_available(MM* mm) {
1298   size_t available;
1299   if (mm != NULL && mm_lock(mm, MM_LOCK_RD)) {
1300     available = mm->available;
1301     mm_unlock(mm);
1302     return available;
1303   }
1304   return 0;
1305 }
1306
1307 const char* mm_shm_type() {
1308   return MM_SHM_TYPE;
1309 }
1310
1311 const char* mm_sem_type() {
1312   return MM_SEM_TYPE;
1313 }
1314
1315 int mm_protect(MM* mm, int mode) {
1316 #ifdef HAVE_MPROTECT
1317   int pmode = 0;
1318   if (mode & MM_PROT_NONE) {
1319     pmode |= PROT_NONE;
1320   }
1321   if (mode & MM_PROT_READ) {
1322     pmode |= PROT_READ;
1323   }
1324   if (mode & MM_PROT_WRITE) {
1325     pmode |= PROT_WRITE;
1326   }
1327   if (mode & MM_PROT_EXEC) {
1328     pmode |= PROT_EXEC;
1329   }
1330   return (mprotect(mm, mm->size, pmode) == 0);
1331 #endif
1332   return 0;
1333 }
1334
1335 #ifdef MM_TEST_SHM
1336 int main() {
1337   char key[] = "/tmp/mm";
1338   size_t size = 32*1024*1024;
1339   MM *mm = mm_create(size, key);
1340   if (mm == NULL) {
1341     return 1;
1342   }
1343   mm_destroy(mm);
1344   return 0;
1345 }
1346 #endif
1347
1348 #ifdef MM_TEST_SEM
1349 int main() {
1350   int ret = 0;
1351   char key[] = "/tmp/mm";
1352   size_t size = 1*1024*1024;
1353   MM *mm = mm_create(size, key);
1354   if (mm == NULL) {