root/eaccelerator/branches/0.9.4/mm.c

Revision 218, 30.6 kB (checked in by bart, 2 years ago)

Removed some includes in debug.c and added on in mm.c

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