root/eaccelerator/tags/0.9.4-rc1/mm.c

Revision 131, 30.3 kB (checked in by zoeloelip, 3 years ago)

README updates
i386 <-> x86_64 spinlock test

  • 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
325   if ((lock->semid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666)) < 0) {
326     return 0;
327   }
328   arg.val = 1;
329   do {
330     rc = semctl(lock->semid, 0, SETVAL, arg);
331   } while (rc < 0 && errno == EINTR);
332   if (rc < 0) {
333     do {
334       semctl(lock->semid, 0, IPC_RMID, 0);
335     } while (rc < 0 && errno == EINTR);
336     return 0;
337   }
338   return 1;
339 }
340
341 static int mm_do_lock(mm_mutex* lock, int kind) {
342   int rc;
343   struct sembuf op;
344
345   op.sem_num = 0;
346   op.sem_op  = -1;
347   op.sem_flg = SEM_UNDO;
348   do {
349     rc = semop(lock->semid, &op, 1);
350   } while (rc < 0 && errno == EINTR);
351   return (rc == 0);
352 }
353
354 static int mm_do_unlock(mm_mutex* lock) {
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 void mm_destroy_lock(mm_mutex* lock) {
368   int rc;
369   do {
370     rc = semctl(lock->semid, 0, IPC_RMID, 0);
371   } while (rc < 0 && errno == EINTR);
372 }
373
374 #elif defined(MM_SEM_FCNTL)
375
376 #define MM_SEM_TYPE "fcntl"
377
378 typedef struct mm_mutex {
379   int fd;
380 } mm_mutex;
381
382 static int mm_init_lock(const char* key, mm_mutex* lock) {
383   char s[MAXPATHLEN];
384
385   strncpy(s,key,MAXPATHLEN-1);
386   strxcat(s,".sem.XXXXXX",MAXPATHLEN);
387   lock->fd =mkstemp(s);
388   if (lock->fd != -1) {
389     unlink(s);
390   }
391   return (lock->fd != -1);
392 }
393
394 static int mm_do_lock(mm_mutex* lock, int kind) {
395   int rc;
396   struct flock l;
397   l.l_whence   = SEEK_SET;
398   l.l_start    = 0;
399   l.l_len      = 0;
400   l.l_pid      = 0;
401   if (kind == MM_LOCK_RD) {
402     l.l_type     = F_RDLCK;
403   } else {
404     l.l_type     = F_WRLCK;
405   }
406   do {
407     rc = fcntl(lock->fd, F_SETLKW, &l);
408   } while (rc < 0 && errno == EINTR);
409   return (rc == 0);
410 }
411
412 static int mm_do_unlock(mm_mutex* lock) {
413   int rc;
414   struct flock l;
415   l.l_whence   = SEEK_SET;
416   l.l_start    = 0;
417   l.l_len      = 0;
418   l.l_pid      = 0;
419   l.l_type     = F_UNLCK;
420   do {
421     rc = fcntl(lock->fd, F_SETLKW, &l);
422   } while (rc < 0 && errno == EINTR);
423   return (rc == 0);
424 }
425
426 static void mm_destroy_lock(mm_mutex* lock) {
427   close(lock->fd);
428 }
429
430 /* ######################################################################### */
431
432 #elif defined(MM_SEM_FLOCK)
433
434 /* this method is not thread safe */
435
436 #define MM_SEM_TYPE "flock"
437
438 static int   mm_flock_fd  = -1;
439 static pid_t mm_flock_pid = -1;
440
441 typedef struct mm_mutex {
442   char filename[MAXPATHLEN];
443 } mm_mutex;
444
445 static int mm_init_lock(const char* key, mm_mutex* lock) {
446   strncpy(lock->filename,key,MAXPATHLEN-1);
447   strxcat(lock->filename,".sem.XXXXXX",MAXPATHLEN);
448   mm_flock_fd =mkstemp(lock->filename);
449   if (mm_flock_fd != -1) {
450 #if defined(F_SETFD) && defined(FD_CLOEXEC)
451     fcntl(mm_flock_fd, F_SETFD, FD_CLOEXEC);
452 #endif
453     mm_flock_pid = getpid();
454     return 1;
455   }
456   return 0;
457 }
458
459 static int mm_do_lock(mm_mutex* lock, int kind) {
460   pid_t pid = getpid();
461   int rc;
462   if (kind == MM_LOCK_RD) {
463     kind = LOCK_SH;
464   } else {
465     kind = LOCK_EX;
466   }
467
468   if (mm_flock_fd == -1 || mm_flock_pid != pid) {
469     mm_flock_fd = open(lock->filename, O_RDWR, S_IRUSR | S_IWUSR);
470     if (mm_flock_fd == -1) {
471       return 0;
472     }
473 #if defined(F_SETFD) && defined(FD_CLOEXEC)
474     fcntl(mm_flock_fd, F_SETFD, FD_CLOEXEC);
475 #endif
476     mm_flock_pid = pid;
477   }
478   do {
479     rc = flock(mm_flock_fd, kind);
480   } while (rc < 0 && errno == EINTR);
481   return (rc == 0);
482 }
483
484 static int mm_do_unlock(mm_mutex* lock) {
485   int rc;
486   if (mm_flock_fd == -1) {
487     mm_flock_fd = open(lock->filename, O_RDWR, S_IRUSR | S_IWUSR);
488     if (mm_flock_fd == -1) {
489       return 0;
490     }
491   }
492   do {
493     rc = flock(mm_flock_fd, LOCK_UN);
494   } while (rc < 0 && errno == EINTR);
495   return (rc == 0);
496 }
497
498 static void mm_destroy_lock(mm_mutex* lock) {
499   close(mm_flock_fd);
500   unlink(lock->filename);
501 }
502
503 /* ######################################################################### */
504
505 #elif defined(MM_SEM_BEOS)
506
507 #define MM_SEM_TYPE "beos"
508 #error "Semophore type (MM_SEM_BEOS) is not implemented"
509
510 #elif defined(MM_SEM_OS2)
511
512 #define MM_SEM_TYPE "os2"
513 #error "Semophore type (MM_SEM_OS2) is not implemented"
514
515 #elif defined(MM_SEM_WIN32)
516
517 #define MM_SEM_TYPE "win32"
518 #define MM_SEM_CAN_ATTACH
519
520 typedef struct mm_mutex {
521   HANDLE hMutex;
522 } mm_mutex;
523
524 static mm_mutex g_lock;
525
526 static int mm_attach_lock(const char* key, mm_mutex* lock) {
527   char* ch;
528   char name[256];
529   HANDLE hMutex;
530
531   strncpy(name, key, 255);
532   strxcat(name, ".sem", 255);
533   for (ch = name; *ch; ++ch) {
534     if (*ch == ':' || *ch == '/' || *ch == '\\') {
535       *ch = '_';
536     }
537   }
538   g_lock.hMutex = hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, name);
539   if (!g_lock.hMutex) {
540     return 0;
541   }
542   return 1;
543 }
544
545 static int mm_init_lock(const char* key, mm_mutex* lock) {
546   char* ch;
547   char name[256];
548   strncpy(name, key, 255);
549   strxcat(name, ".sem", 255);
550   for (ch = name; *ch; ++ch) {
551     if (*ch == ':' || *ch == '/' || *ch == '\\') {
552       *ch = '_';
553     }
554   }
555   g_lock.hMutex = CreateMutex(NULL, FALSE, name);
556   if (!g_lock.hMutex) {
557     return 0;
558   }
559   return 1;
560 }
561
562 static void mm_destroy_lock(mm_mutex* lock) {
563   CloseHandle(g_lock.hMutex);
564 }
565
566 static int mm_do_lock(mm_mutex* lock, int kind) {
567   DWORD rv;
568
569   rv = WaitForSingleObject(g_lock.hMutex, INFINITE);
570
571   if (rv == WAIT_OBJECT_0 || rv == WAIT_ABANDONED) {
572     return 1;
573   }
574   return 0;
575 }
576
577 static int mm_do_unlock(mm_mutex* lock) {
578   if (ReleaseMutex(g_lock.hMutex) == 0) {
579     return 0;
580   }
581   return 1;
582 }
583
584 /* ######################################################################### */
585
586 #elif defined(MM_SEM_NONE)
587
588 #define MM_SEM_TYPE "none"
589 #define MM_SEM_CAN_ATTACH
590
591
592 typedef struct mm_mutex {
593   int semid;
594 } mm_mutex;
595
596 static int mm_attach_lock(const char* key, mm_mutex* lock) {
597   return 1;
598 }
599
600 static int mm_init_lock(const char* key, mm_mutex* lock) {
601   return 1;
602 }
603
604 static void mm_destroy_lock(mm_mutex* lock) {
605 }
606
607 static int mm_do_lock(mm_mutex* lock, int kind) {
608   return 1;
609 }
610
611 static int mm_do_unlock(mm_mutex* lock) {
612   return 1;
613 }
614
615 #else
616 #  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"
617 #endif
618
619 int mm_lock(MM* mm, int kind) {
620   if (mm_do_lock(mm->lock, kind)) {
621     return 1;
622   } else {
623 #if !defined(MM_TEST_SEM) && !defined(MM_TEST_SHM)
624     ea_debug_error("eAccelerator: Could not lock!\n");
625 #endif
626     return 0;
627   }
628 }
629
630 int mm_unlock(MM* mm) {
631   if (mm_do_unlock(mm->lock)) {
632     return 1;
633   } else {
634 #if !defined(MM_TEST_SEM) && !defined(MM_TEST_SHM)
635     ea_debug_error("eAccelerator: Could not release lock!\n");
636 #endif
637     return 0;
638   }
639 }
640
641 /* Shared Memory Implementations */
642
643 /* ######################################################################### */
644
645 #if defined(MM_SHM_IPC)
646
647 #define MM_SHM_TYPE "sysvipc"
648
649 #ifndef SHM_R
650 # define SHM_R 0444 /* read permission */
651 #endif
652 #ifndef SHM_W
653 # define SHM_W 0222 /* write permission */
654 #endif
655
656 static MM* mm_create_shm(const char* key, size_t size) {
657   int fd;
658   void** segment = NULL;
659   if ((fd = shmget(IPC_PRIVATE, size, (IPC_CREAT | SHM_R | SHM_W))) != -1) {
660     MM* p;
661     if ((p = (MM*)shmat(fd, NULL, 0)) != ((void *)-1)) {
662       struct shmid_ds shmbuf;
663       if (shmctl(fd, IPC_STAT, &shmbuf) == 0) {
664         shmbuf.shm_perm.uid = getuid();
665         shmbuf.shm_perm.gid = getgid();
666         if (shmctl(fd, IPC_SET, &shmbuf) == 0) {
667           shmctl(fd, IPC_RMID, NULL);
668           p->size = size;
669           segment = (void**)((char*)p+sizeof(MM));
670           *segment = (void*)-1;
671           segment++;
672           p->start = segment;
673           return p;
674         }
675       }
676       shmdt(p);
677     }
678     shmctl(fd, IPC_RMID, NULL);
679   } else {
680     /* can't get one shared memory segment, trying to get several */
681     size_t seg_size = 1024*1024;
682     size_t orig_size = size;
683     void*  p;
684     MM*    root = NULL;
685     char*  prev = NULL;
686
687     while (seg_size <= size/2) {
688       seg_size *= 2;
689     }
690     while ((fd = shmget(IPC_PRIVATE, seg_size, (IPC_CREAT | SHM_R | SHM_W))) == -1) {
691       if (seg_size <= 1024*1024) {
692         return (MM*)-1;
693       }
694       seg_size /= 2;
695     }
696     while (size > 0) {
697       if (fd != -1 ||
698           (fd = shmget(IPC_PRIVATE, (size > seg_size)?seg_size:size, (IPC_CREAT | SHM_R | SHM_W))) != -1) {
699         if ((p = (void *)shmat(fd, prev?(prev+seg_size):NULL, 0)) != ((void *)-1) &&
700             (prev == NULL || prev + seg_size == p)) {
701           struct shmid_ds shmbuf;
702 /*???
703           memset(p, 0, (size > seg_size)?seg_size:size);
704 */
705           if (shmctl(fd, IPC_STAT, &shmbuf) == 0) {
706             shmbuf.shm_perm.uid = getuid();
707             shmbuf.shm_perm.gid = getgid();
708             if (shmctl(fd, IPC_SET, &shmbuf) == 0) {
709               shmctl(fd, IPC_RMID, NULL);
710               if (root == NULL) {
711                 root = (MM*)p;
712                 segment = (void**)((char*)p+sizeof(MM));
713               } else {
714                 *segment = p;
715                 segment++;
716               }
717               prev = (char*)p;
718               fd = -1;
719               if (size > seg_size) {
720                 size -= seg_size;
721               } else {
722                 size = 0;
723               }
724               continue;
725             }
726           }
727           shmdt(p);
728         }
729         shmctl(fd, IPC_RMID, NULL);
730       }
731       if (root != NULL) {
732         while (segment > (void**)((char*)root+sizeof(MM))) {
733           segment--;
734           shmdt(*segment);
735         }
736       }
737       shmdt(root);
738       return (MM*)-1;
739     }
740     *segment = (void*)-1;
741     segment++;
742     root->size  = orig_size;
743     root->start = (void*)segment;
744     return root;
745   }
746   return (MM*)-1;
747 }
748
749 static void mm_destroy_shm(MM* mm) {
750   void** segment = (void**)((char*)mm+sizeof(MM));
751   while (*segment != (void*)-1) {
752     shmdt(*segment);
753     ++segment;
754   }
755   shmdt(mm);
756 }
757
758 /* ######################################################################### */
759
760 #elif defined(MM_SHM_MMAP_ANON)
761
762 #define MM_SHM_TYPE "mmap_anon"
763
764 #ifndef MAP_ANON
765 #  ifdef MAP_ANONYMOUS
766 #    define MAP_ANON MAP_ANONYMOUS
767 #  endif
768 #endif
769
770 static MM* mm_create_shm(const char* key, size_t size) {
771   MM* p;
772   p = (MM*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
773   if (p != (MM*)-1) {
774     p->size = size;
775     p->start = (char*)p+sizeof(MM);
776   }
777   return p;
778 }
779
780 static void mm_destroy_shm(MM* mm) {
781   munmap(mm,mm->size);
782 }
783
784 /* ######################################################################### */
785
786 #elif defined(MM_SHM_MMAP_ZERO)
787
788 #define MM_SHM_TYPE "mmap_zero"
789
790 static MM* mm_create_shm(const char* key, size_t size) {
791   MM* p;
792   int fd = open("/dev/zero", O_RDWR, S_IRUSR | S_IWUSR);
793   if (fd == -1) {
794     return (MM*)-1;
795   }
796   p = (MM*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
797   close(fd);
798   if (p != (MM*)-1) {
799     p->size = size;
800     p->start = (char*)p+sizeof(MM);
801   }
802   return p;
803 }
804
805 static void mm_destroy_shm(MM* mm) {
806   munmap(mm,mm->size);
807 }
808
809 /* ######################################################################### */
810
811 #elif defined(MM_SHM_MMAP_POSIX)
812
813 #define MM_SHM_TYPE "mmap_posix"
814
815 /* Not Tested */
816
817 static MM* mm_create_shm(const char* key, size_t size) {
818   MM* p;
819   int fd;
820   char s[MAXPATHLEN];
821
822   strncpy(s,key,MAXPATHLEN-1);
823   strxcat(s,".shm.XXXXXX",MAXPATHLEN);
824   if (mktemp(s) == NULL) {
825     return (MM*)-1;
826   }
827   if ((fd = shm_open(s, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) == -1) {
828     return (MM*)-1;
829   }
830   if (ftruncate(fd, size) < 0) {
831     close(fd);
832     shm_unlink(s);
833     return (MM*)-1;
834   }
835   p = (MM*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
836   shm_unlink(s);
837   close(fd);
838   if (p != (MM*)-1) {
839     p->size = size;
840     p->start = (char*)p+sizeof(MM);
841   }
842   return p;
843 }
844
845 static void mm_destroy_shm(MM* mm) {
846   munmap(mm,mm->size);
847 }
848
849 /* ######################################################################### */
850
851 #elif defined(MM_SHM_MMAP_FILE)
852
853 #define MM_SHM_TYPE "mmap_file"
854
855 static MM* mm_create_shm(const char* key, size_t size) {
856   MM* p;
857   int fd;
858   char s[MAXPATHLEN];
859
860   strncpy(s,key,MAXPATHLEN-1);
861   strxcat(s,".shm.XXXXXX",MAXPATHLEN);
862   fd = mkstemp(s);
863   if (fd < 0) {
864     return (MM*)-1;
865   }
866   if (ftruncate(fd, size) < 0) {
867     return (MM*)-1;
868   }
869   p = (MM*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
870   close(fd);
871   unlink(s);
872   if (p != (MM*)-1) {
873     p->size = size;
874     p->start = (char*)p+sizeof(MM);
875   }
876   return p;
877 }
878
879 static void mm_destroy_shm(MM* mm) {
880   munmap(mm,mm->size);
881 }
882
883 /* ######################################################################### */
884
885 #elif defined(MM_SHM_BEOS)
886
887 #define MM_SHM_TYPE "beos"
888 #error "Shared memeory type (MM_SHM_BEOS) is not implemented"
889
890 /* ######################################################################### */
891
892 #elif defined(MM_SHM_OS2)
893
894 #define MM_SHM_TYPE "os2"
895 #error "Shared memeory type (MM_SHM_OS2) is not implemented"
896
897 /* ######################################################################### */
898
899 #elif defined(MM_SHM_WIN32)
900
901 #define MM_SHM_TYPE "win32"
902 #define MM_SHM_CAN_ATTACH
903
904 static MM* mm_attach_shm(const char* key, size_t size) {
905   HANDLE  shm_handle;
906   MM*     mm;
907   MM*     addr;
908   MM**    addr_ptr;
909   char    s[MAXPATHLEN];
910   char*   ch;
911
912
913   strcpy(s,key);
914   for (ch = s; *ch; ++ch) {
915     if (*ch == ':' || *ch == '/' || *ch == '\\') {
916       *ch = '_';
917     }
918   }
919
920   shm_handle = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, s);
921   if (shm_handle) {
922     mm = (MM*)MapViewOfFile(shm_handle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
923     if (mm == NULL) {
924       return (MM*)-1;
925     }
926 /*
927     if (mm->size != size) {
928       UnmapViewOfFile(mm);
929       CloseHandle(shm_handle);
930       return (MM*)-1;
931     }
932 */
933     addr_ptr = (MM**)(((char*)mm)+sizeof(MM));
934     addr = *addr_ptr;
935     if (addr != mm) {
936       UnmapViewOfFile(mm);
937       mm = (MM*)MapViewOfFileEx(shm_handle, FILE_MAP_ALL_ACCESS, 0, 0, 0, addr);
938       if (mm == NULL) {
939         return (MM*)-1;
940       }
941     }
942 /*  CloseHandle(shm_handle);*/
943     return mm;
944   }
945   return (MM*)-1;
946 }
947
948 static MM* mm_create_shm(const char* key, size_t size) {
949   HANDLE  shm_handle;
950   MM*     mm;
951   MM**    addr_ptr;
952   char    s[MAXPATHLEN];
953   char*   ch;
954
955
956   strcpy(s,key);
957   for (ch = s; *ch; ++ch) {
958     if (*ch == ':' || *ch == '/' || *ch == '\\') {
959       *ch = '_';
960     }
961   }
962
963   shm_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, s);
964   if (!shm_handle) {
965     return (MM*)-1;
966   }
967   mm = (MM*)MapViewOfFileEx(shm_handle, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
968   if (mm == NULL) {
969     return (MM*)-1;
970   }
971   addr_ptr = (MM**)(((char*)mm)+sizeof(MM));
972   *addr_ptr = mm;
973   mm->size = size;
974   mm->start = ((char*)mm)+sizeof(MM)+sizeof(void*);
975
976 /*  CloseHandle(shm_handle);*/
977   return mm;
978 }
979
980 static void mm_destroy_shm(MM* mm) {
981   UnmapViewOfFile(mm);
982 }
983
984 /* ######################################################################### */
985
986 #elif defined(MM_SHM_MALLOC)
987
988 #define MM_SHM_TYPE "malloc"
989
990 static void* mm_create_shm(const char* key, size_t size) {
991   MM* p = (MM*)malloc(sizeof(MM));
992   if (p == NULL) {
993     return (MM*)-1;
994   }
995   p->size  = size;
996   p->start = NULL;
997   return p;
998 }
999
1000 static void mm_destroy_shm(MM* mm) {
1001   free(mm);
1002 }
1003
1004 /* ######################################################################### */
1005
1006 #else
1007 #define MM_SHM_TYPE "none"
1008 #  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"
1009 #endif
1010
1011 #ifdef MM_SHM_MALLOC
1012 static void mm_init(MM* mm) {
1013   mm->available = mm->size - sizeof(MM);
1014   mm->lock = malloc(sizeof(mm_mutex));
1015 }
1016
1017 void* mm_malloc_nolock(MM* mm, size_t size) {
1018   if (size > 0) {
1019     mm_mem_head *p = NULL;
1020     if (mm->available >= MM_SIZE(size)) {
1021       p = malloc(MM_SIZE(size));
1022       if (p != NULL) {
1023         p->size = MM_SIZE(size);
1024         mm->available -= MM_SIZE(size);
1025       }
1026     }
1027     if (p != NULL) {
1028       return HEAD_TO_PTR(p);
1029     }
1030   }
1031   return NULL;
1032 }
1033
1034 void mm_free_nolock(MM* mm, void* x) {
1035   if (x != NULL) {
1036     mm_mem_head *p;
1037     p = PTR_TO_HEAD(x);
1038     mm->available += p->size;
1039     free(p);
1040   }
1041 }
1042
1043 size_t mm_maxsize(MM* mm) {
1044   size_t ret;
1045   if (!mm_lock(mm, MM_LOCK_RD)) {
1046     return 0;
1047   }
1048   ret = mm->available - MM_SIZE(0);
1049   mm_unlock(mm);
1050   return ret;
1051 }
1052
1053 #else
1054 static void mm_init(MM* mm) {
1055   mm->start = MM_ALIGN(mm->start);
1056   mm->attach_addr = (void*)mm;
1057   mm->lock = mm->start;
1058   mm->start = MM_ALIGN((void*)(((char*)(mm->start)) + sizeof(mm_mutex)));
1059   mm->available = mm->size - (((char*)(mm->start))-(char*)mm);
1060   mm->free_list = (mm_free_bucket*)mm->start;
1061   mm->free_list->size = mm->available;
1062   mm->free_list->next = NULL;
1063 }
1064
1065 void* mm_malloc_nolock(MM* mm, size_t size) {
1066   if (size > 0) {
1067     mm_mem_head* x = NULL;
1068     size_t realsize = (size_t)MM_ALIGN(MM_SIZE(size));
1069     if (realsize <= mm->available) {
1070       /* Search for free bucket */
1071       mm_free_bucket* p = mm->free_list;
1072       mm_free_bucket* q = NULL;
1073       mm_free_bucket* best = NULL;
1074       mm_free_bucket* best_prev = NULL;
1075       while (p != NULL) {
1076         if (p->size == realsize) {
1077           /* Found free bucket with the same size */
1078           if (q == NULL) {
1079             mm->free_list = p->next;
1080             x = (mm_mem_head*)p;
1081           } else {
1082             q->next = p->next;
1083             x = (mm_mem_head*)p;
1084           }
1085           break;
1086         } else if (p->size > realsize && (best == NULL || best->size > p->size)) {
1087           /* Found best bucket (smallest bucket with the grater size) */
1088           best = p;
1089           best_prev = q;
1090         }
1091         q = p;
1092         p = p->next;
1093       }
1094       if (x == NULL && best != NULL) {
1095         if (best->size-realsize < sizeof(mm_free_bucket)) {
1096           realsize = best->size;
1097           x = (mm_mem_head*)best;
1098           if (best_prev == NULL) {
1099             mm->free_list = best->next;
1100           } else {
1101             best_prev->next = best->next;
1102           }
1103         } else {
1104           if (best_prev == NULL) {
1105             mm->free_list = (mm_free_bucket*)((char*)best + realsize);
1106             mm->free_list->size = best->size-realsize;
1107             mm->free_list->next = best->next;
1108           } else {
1109             best_prev->next = (mm_free_bucket*)((char*)best + realsize);
1110             best_prev->next->size = best->size-realsize;
1111             best_prev->next->next = best->next;
1112           }
1113           best->size = realsize;
1114           x = (mm_mem_head*)best;
1115         }
1116       }
1117       if (x != NULL) {
1118         mm->available -= realsize;
1119       }
1120     }
1121     if (x != NULL) {
1122       return HEAD_TO_PTR(x);
1123     }
1124   }
1125   return NULL;
1126 }
1127
1128 void mm_free_nolock(MM* mm, void* x) {
1129   if (x != NULL) {
1130     if (x >= mm->start && x < (void*)((char*)mm + mm->size)) {
1131       mm_mem_head *p = PTR_TO_HEAD(x);
1132       size_t size = p->size;
1133       if ((char*)p+size <= (char*)mm + mm->size) {
1134         mm_free_bucket* b = (mm_free_bucket*)p;
1135         b->next = NULL;
1136         if (mm->free_list == NULL) {
1137           mm->free_list = b;
1138         } else {
1139           mm_free_bucket* q = mm->free_list;
1140           mm_free_bucket* prev = NULL;
1141           mm_free_bucket* next = NULL;
1142           while (q != NULL) {
1143             if (b < q) {
1144               next = q;
1145               break;
1146             }
1147             prev = q;
1148             q = q->next;
1149           }
1150           if (prev != NULL && (char*)prev+prev->size == (char*)b) {
1151             if ((char*)next == (char*)b+size) {
1152               /* merging with prev and next */
1153               prev->size += size + next->size;
1154               prev->next = next->next;
1155             } else {
1156               /* merging with prev */
1157               prev->size += size;
1158             }
1159           } else {
1160             if ((char*)next == (char*)b+size) {
1161               /* merging with next */
1162               b->size += next->size;
1163               b->next = next->next;
1164             } else {
1165               /* don't merge */
1166               b->next = next;
1167             }
1168             if (prev != NULL) {
1169               prev->next = b;
1170             } else {
1171               mm->free_list = b;
1172             }
1173           }
1174         }
1175         mm->available += size;
1176       }
1177     }
1178   }
1179 }
1180
1181 size_t mm_maxsize(MM* mm) {
1182   size_t ret = MM_SIZE(0);
1183   mm_free_bucket* p;
1184   if (!mm_lock(mm, MM_LOCK_RD)) {
1185     return 0;
1186   }
1187   p = mm->free_list;
1188   while (p != NULL) {
1189     if (p->size > ret) {
1190       ret = p->size;
1191     }
1192     p = p->next;
1193   }
1194   mm_unlock(mm);
1195   return ret - MM_SIZE(0);
1196 }
1197 #endif
1198
1199 void* mm_malloc_lock(MM* mm, size_t size) {
1200   void *ret;
1201   if (!mm_lock(mm, MM_LOCK_RW)) {
1202     return NULL;
1203   }
1204   ret = mm_malloc_nolock(mm,size);
1205   mm_unlock(mm);
1206   return ret;
1207 }
1208
1209 void mm_free_lock(MM* mm, void* x) {
1210   mm_lock(mm, MM_LOCK_RW);
1211   mm_free_nolock(mm,x);
1212   mm_unlock(mm);
1213 }
1214
1215 void mm_set_attach(MM* mm, void* attach_addr) {
1216   mm->attach_addr = attach_addr;
1217 }
1218
1219 void* mm_attach(size_t size, const char* key) {
1220 #ifdef MM_SHM_CAN_ATTACH
1221   MM* mm = mm_attach_shm(key, size);
1222   if (mm == (MM*)-1) {
1223     return NULL;
1224   }
1225 #ifdef MM_SEM_CAN_ATTACH
1226   if (!mm_attach_lock(key, mm->lock)) {
1227     mm_destroy_shm(mm);
1228     return NULL;
1229   }
1230 #endif
1231   return mm->attach_addr;
1232 #else
1233   return NULL;
1234 #endif
1235 }
1236
1237 MM* mm_create(size_t size, const char* key) {
1238   MM* p;
1239   if (size == 0) {
1240     size = 32 * 1024 * 1024;
1241   }
1242   p = mm_create_shm(key, size);
1243   if (p == (MM*)-1) {
1244     return NULL;
1245   }
1246   mm_init(p);
1247   if (p->lock == NULL) {
1248     mm_destroy_shm(p);
1249     return NULL;
1250   }
1251   if (!mm_init_lock(key, p->lock)) {
1252     mm_destroy_shm(p);
1253     return NULL;
1254   }
1255   return p;
1256 }
1257
1258 void mm_destroy(MM* mm) {
1259   if (mm != NULL) {
1260     mm_destroy_lock(mm->lock);
1261     mm_destroy_shm(mm);
1262   }
1263 }
1264
1265 size_t mm_size(MM* mm) {
1266   if (mm != NULL) {
1267     return mm->size;
1268   }
1269   return 0;
1270 }
1271
1272 size_t mm_sizeof(MM* mm, void* x) {
1273   mm_mem_head *p;
1274   size_t ret;
1275   if (mm == NULL || x == NULL || !mm_lock(mm, MM_LOCK_RD)) {
1276     return 0;
1277   }
1278   p = PTR_TO_HEAD(x);
1279   ret = p->size;
1280   mm_unlock(mm);
1281   return ret;
1282 }
1283
1284 size_t mm_available(MM* mm) {
1285   size_t available;
1286   if (mm != NULL && mm_lock(mm, MM_LOCK_RD)) {
1287     available = mm->available;
1288     mm_unlock(mm);
1289     return available;
1290   }
1291   return 0;
1292 }
1293
1294 const char* mm_shm_type() {
1295   return MM_SHM_TYPE;
1296 }
1297
1298 const char* mm_sem_type() {
1299   return MM_SEM_TYPE;
1300 }
1301
1302 int mm_protect(MM* mm, int mode) {
1303 #ifdef HAVE_MPROTECT
1304   int pmode = 0;
1305   if (mode & MM_PROT_NONE) {
1306     pmode |= PROT_NONE;
1307   }
1308   if (mode & MM_PROT_READ) {
1309     pmode |= PROT_READ;
1310   }
1311   if (mode & MM_PROT_WRITE) {
1312     pmode |= PROT_WRITE;
1313   }
1314   if (mode & MM_PROT_EXEC) {
1315     pmode |= PROT_EXEC;
1316   }
1317   return (mprotect(mm, mm->size, pmode) == 0);
1318 #endif
1319   return 0;
1320 }
1321
1322 #ifdef MM_TEST_SHM
1323 int main() {
1324   char key[] = "/tmp/mm";
1325   size_t size = 32*1024*1024;
1326   MM *mm = mm_create(size, key);
1327   if (mm == NULL) {
1328     return 1;
1329   }
1330   mm_destroy(mm);
1331   return 0;
1332 }
1333 #endif
1334
1335 #ifdef MM_TEST_SEM
1336 int main() {
1337   int ret = 0;
1338   char key[] = "/tmp/mm";
1339   size_t size = 1*1024*1024;
1340   MM *mm = mm_create(size, key);
1341   if (mm == NULL) {
1342     return 1;
1343   }
1344   if (!mm_lock(mm, MM_LOCK_RW)) {
1345     ret = 1;
1346   }
1347   if (!mm_unlock(mm)) {
1348     ret = 1;
1349   }
1350   if (!mm_lock(mm, MM_LOCK_RD)) {
1351     ret = 1;
1352   }
1353   if (!mm_unlock(mm)) {
1354     ret = 1;
1355   }
1356   mm_destroy(mm);
1357   return ret;
1358 }
135