root/eaccelerator/tags/0.9.5-beta2/mm.c

Revision 197, 30.8 kB (checked in by bart, 3 years ago)

* Silenced the unlock function for win32 until a windows developer can

take a look at it.

* Updated the README a little more for the new control panel
* Added release notes for beta2
* Bumped up version to 0.9.5-beta2
* Released beta2

  • Property svn:eol-style set to native
  • Property svn:keywords set to svn:eol-style
Line 
1 /*
2    +----------------------------------------------------------------------+
3    | eAccelerator project                                                 |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 2004 - 2006 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    $Id: mm.c 176 2006-03-05 12:18:54Z bart $
26 */
27
28 /* libmm replacement */
29
30 #if !defined(MM_TEST_SHM) && !defined(MM_TEST_SEM)
31 # ifdef HAVE_CONFIG_H
32 #  include "config.h"
33 # endif
34 # include "php.h"
35 #endif
36
37 #ifdef WIN32
38 #  if 1
39 #    define MM_SHM_WIN32
40 #    define MM_SEM_WIN32
41 #  else
42 #    define MM_SHM_MALLOC
43 #    define MM_SEM_NONE
44 #  endif
45 #endif
46
47 #if !defined(MM_TEST_SEM) && !defined(MM_TEST_SHM)
48 # include "debug.h"
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       // Releasing the mutex doesn't seem to work under windows. It gives some
595       // extremely obscure error code. Locking seems to work though. Because this
596       // flood the error log of the win32 users we are not going to return 0 here
597       // until a windows dev has found the problem.
598   }
599   return 1;
600 }
601
602 /* ######################################################################### */
603
604 #elif defined(MM_SEM_NONE)
605
606 #define MM_SEM_TYPE "none"
607 #define MM_SEM_CAN_ATTACH
608
609
610 typedef struct mm_mutex {
611   int semid;
612 } mm_mutex;
613
614 static int mm_attach_lock(const char* key, mm_mutex* lock) {
615   return 1;
616 }
617
618 static int mm_init_lock(const char* key, mm_mutex* lock) {
619   return 1;
620 }
621
622 static void mm_destroy_lock(mm_mutex* lock) {
623 }
624
625 static int mm_do_lock(mm_mutex* lock, int kind) {
626   return 1;
627 }
628
629 static int mm_do_unlock(mm_mutex* lock) {
630   return 1;
631 }
632
633 #else
634 #  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"
635 #endif
636
637 int mm_lock(MM* mm, int kind) {
638   if (mm_do_lock(mm->lock, kind)) {
639     return 1;
640   } else {
641 #if !defined(MM_TEST_SEM) && !defined(MM_TEST_SHM)
642     ea_debug_error("eAccelerator: Could not lock!\n");
643 #endif
644     return 0;
645   }
646 }
647
648 int mm_unlock(MM* mm) {
649   if (mm_do_unlock(mm->lock)) {
650     return 1;
651   } else {
652 #if !defined(MM_TEST_SEM) && !defined(MM_TEST_SHM)
653     ea_debug_error("eAccelerator: Could not release lock!\n");
654 #endif
655     return 0;
656   }
657 }
658
659 /* Shared Memory Implementations */
660
661 /* ######################################################################### */
662
663 #if defined(MM_SHM_IPC)
664
665 #define MM_SHM_TYPE "sysvipc"
666
667 #ifndef SHM_R
668 # define SHM_R 0444 /* read permission */
669 #endif
670 #ifndef SHM_W
671 # define SHM_W 0222 /* write permission */
672 #endif
673
674 static MM* mm_create_shm(const char* key, size_t size) {
675   int fd;
676   void** segment = NULL;
677   if ((fd = shmget(IPC_PRIVATE, size, (IPC_CREAT | SHM_R | SHM_W))) != -1) {
678     MM* p;
679     if ((p = (MM*)shmat(fd, NULL, 0)) != ((void *)-1)) {
680       struct shmid_ds shmbuf;
681       if (shmctl(fd, IPC_STAT, &shmbuf) == 0) {
682         shmbuf.shm_perm.uid = getuid();
683         shmbuf.shm_perm.gid = getgid();
684         if (shmctl(fd, IPC_SET, &shmbuf) == 0) {
685           shmctl(fd, IPC_RMID, NULL);
686           p->size = size;
687           segment = (void**)((char*)p+sizeof(MM));
688           *segment = (void*)-1;
689           segment++;
690           p->start = segment;
691           return p;
692         }
693       }
694       shmdt(p);
695     }
696     shmctl(fd, IPC_RMID, NULL);
697   } else {
698     /* can't get one shared memory segment, trying to get several */
699     size_t seg_size = 1024*1024;
700     size_t orig_size = size;
701     void*  p;
702     MM*    root = NULL;
703     char*  prev = NULL;
704
705     while (seg_size <= size/2) {
706       seg_size *= 2;
707     }
708     while ((fd = shmget(IPC_PRIVATE, seg_size, (IPC_CREAT | SHM_R | SHM_W))) == -1) {
709       if (seg_size <= 1024*1024) {
710         return (MM*)-1;
711       }
712       seg_size /= 2;
713     }
714     while (size > 0) {
715       if (fd != -1 ||
716           (fd = shmget(IPC_PRIVATE, (size > seg_size)?seg_size:size, (IPC_CREAT | SHM_R | SHM_W))) != -1) {
717         if ((p = (void *)shmat(fd, prev?(prev+seg_size):NULL, 0)) != ((void *)-1) &&
718             (prev == NULL || prev + seg_size == p)) {
719           struct shmid_ds shmbuf;
720 /*???
721           memset(p, 0, (size > seg_size)?seg_size:size);
722 */
723           if (shmctl(fd, IPC_STAT, &shmbuf) == 0) {
724             shmbuf.shm_perm.uid = getuid();
725             shmbuf.shm_perm.gid = getgid();
726             if (shmctl(fd, IPC_SET, &shmbuf) == 0) {
727               shmctl(fd, IPC_RMID, NULL);
728               if (root == NULL) {
729                 root = (MM*)p;
730                 segment = (void**)((char*)p+sizeof(MM));
731               } else {
732                 *segment = p;
733                 segment++;
734               }
735               prev = (char*)p;
736               fd = -1;
737               if (size > seg_size) {
738                 size -= seg_size;
739               } else {
740                 size = 0;
741               }
742               continue;
743             }
744           }
745           shmdt(p);
746         }
747         shmctl(fd, IPC_RMID, NULL);
748       }
749       if (root != NULL) {
750         while (segment > (void**)((char*)root+sizeof(MM))) {
751           segment--;
752           shmdt(*segment);
753         }
754       }
755       shmdt(root);
756       return (MM*)-1;
757     }
758     *segment = (void*)-1;
759     segment++;
760     root->size  = orig_size;
761     root->start = (void*)segment;
762     return root;
763   }
764   return (MM*)-1;
765 }
766
767 static void mm_destroy_shm(MM* mm) {
768   void** segment = (void**)((char*)mm+sizeof(MM));
769   while (*segment != (void*)-1) {
770     shmdt(*segment);
771     ++segment;
772   }
773   shmdt(mm);
774 }
775
776 /* ######################################################################### */
777
778 #elif defined(MM_SHM_MMAP_ANON)
779
780 #define MM_SHM_TYPE "mmap_anon"
781
782 #ifndef MAP_ANON
783 #  ifdef MAP_ANONYMOUS
784 #    define MAP_ANON MAP_ANONYMOUS
785 #  endif
786 #endif
787
788 static MM* mm_create_shm(const char* key, size_t size) {
789   MM* p;
790   p = (MM*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
791   if (p != (MM*)-1) {
792     p->size = size;
793     p->start = (char*)p+sizeof(MM);
794   }
795   return p;
796 }
797
798 static void mm_destroy_shm(MM* mm) {
799   munmap(mm,mm->size);
800 }
801
802 /* ######################################################################### */
803
804 #elif defined(MM_SHM_MMAP_ZERO)
805
806 #define MM_SHM_TYPE "mmap_zero"
807
808 static MM* mm_create_shm(const char* key, size_t size) {
809   MM* p;
810   int fd = open("/dev/zero", O_RDWR, S_IRUSR | S_IWUSR);
811   if (fd == -1) {
812     return (MM*)-1;
813   }
814   p = (MM*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
815   close(fd);
816   if (p != (MM*)-1) {
817     p->size = size;
818     p->start = (char*)p+sizeof(MM);
819   }
820   return p;
821 }
822
823 static void mm_destroy_shm(MM* mm) {
824   munmap(mm,mm->size);
825 }
826
827 /* ######################################################################### */
828
829 #elif defined(MM_SHM_MMAP_POSIX)
830
831 #define MM_SHM_TYPE "mmap_posix"
832
833 /* Not Tested */
834
835 static MM* mm_create_shm(const char* key, size_t size) {
836   MM* p;
837   int fd;
838   char s[MAXPATHLEN];
839
840   strncpy(s,key,MAXPATHLEN-1);
841   strxcat(s,".shm.XXXXXX",MAXPATHLEN);
842   if (mktemp(s) == NULL) {
843     return (MM*)-1;
844   }
845   if ((fd = shm_open(s, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) == -1) {
846     return (MM*)-1;
847   }
848   if (ftruncate(fd, size) < 0) {
849     close(fd);
850     shm_unlink(s);
851     return (MM*)-1;
852   }
853   p = (MM*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
854   shm_unlink(s);
855   close(fd);
856   if (p != (MM*)-1) {
857     p->size = size;
858     p->start = (char*)p+sizeof(MM);
859   }
860   return p;
861 }
862
863 static void mm_destroy_shm(MM* mm) {
864   munmap(mm,mm->size);
865 }
866
867 /* ######################################################################### */
868
869 #elif defined(MM_SHM_MMAP_FILE)
870
871 #define MM_SHM_TYPE "mmap_file"
872
873 static MM* mm_create_shm(const char* key, size_t size) {
874   MM* p;
875   int fd;
876   char s[MAXPATHLEN];
877
878   strncpy(s,key,MAXPATHLEN-1);
879   strxcat(s,".shm.XXXXXX",MAXPATHLEN);
880   fd = mkstemp(s);
881   if (fd < 0) {
882     return (MM*)-1;
883   }
884   if (ftruncate(fd, size) < 0) {
885     return (MM*)-1;
886   }
887   p = (MM*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
888   close(fd);
889   unlink(s);
890   if (p != (MM*)-1) {
891     p->size = size;
892     p->start = (char*)p+sizeof(MM);
893   }
894   return p;
895 }
896
897 static void mm_destroy_shm(MM* mm) {
898   munmap(mm,mm->size);
899 }
900
901 /* ######################################################################### */
902
903 #elif defined(MM_SHM_BEOS)
904
905 #define MM_SHM_TYPE "beos"
906 #error "Shared memeory type (MM_SHM_BEOS) is not implemented"
907
908 /* ######################################################################### */
909
910 #elif defined(MM_SHM_OS2)
911
912 #define MM_SHM_TYPE "os2"
913 #error "Shared memeory type (MM_SHM_OS2) is not implemented"
914
915 /* ######################################################################### */
916
917 #elif defined(MM_SHM_WIN32)
918
919 #define MM_SHM_TYPE "win32"
920 #define MM_SHM_CAN_ATTACH
921
922 static MM* mm_attach_shm(const char* key, size_t size) {
923   HANDLE  shm_handle;
924   MM*     mm;
925   MM*     addr;
926   MM**    addr_ptr;
927   char    s[MAXPATHLEN];
928   char*   ch;
929
930
931   strcpy(s,key);
932   for (ch = s; *ch; ++ch) {
933     if (*ch == ':' || *ch == '/' || *ch == '\\') {
934       *ch = '_';
935     }
936   }
937
938   shm_handle = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, s);
939   if (shm_handle) {
940     mm = (MM*)MapViewOfFile(shm_handle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
941     if (mm == NULL) {
942       return (MM*)-1;
943     }
944 /*
945     if (mm->size != size) {
946       UnmapViewOfFile(mm);
947       CloseHandle(shm_handle);
948       return (MM*)-1;
949     }
950 */
951     addr_ptr = (MM**)(((char*)mm)+sizeof(MM));
952     addr = *addr_ptr;
953     if (addr != mm) {
954       UnmapViewOfFile(mm);
955       mm = (MM*)MapViewOfFileEx(shm_handle, FILE_MAP_ALL_ACCESS, 0, 0, 0, addr);
956       if (mm == NULL) {
957         return (MM*)-1;
958       }
959     }
960 /*  CloseHandle(shm_handle);*/
961     return mm;
962   }
963   return (MM*)-1;
964 }
965
966 static MM* mm_create_shm(const char* key, size_t size) {
967   HANDLE  shm_handle;
968   MM*     mm;
969   MM**    addr_ptr;
970   char    s[MAXPATHLEN];
971   char*   ch;
972
973
974   strcpy(s,key);
975   for (ch = s; *ch; ++ch) {
976     if (*ch == ':' || *ch == '/' || *ch == '\\') {
977       *ch = '_';
978     }
979   }
980
981   shm_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, s);
982   if (!shm_handle) {
983     return (MM*)-1;
984   }
985   mm = (MM*)MapViewOfFileEx(shm_handle, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
986   if (mm == NULL) {
987     return (MM*)-1;
988   }
989   addr_ptr = (MM**)(((char*)mm)+sizeof(MM));
990   *addr_ptr = mm;
991   mm->size = size;
992   mm->start = ((char*)mm)+sizeof(MM)+sizeof(void*);
993
994 /*  CloseHandle(shm_handle);*/
995   return mm;
996 }
997
998 static void mm_destroy_shm(MM* mm) {
999   UnmapViewOfFile(mm);
1000 }
1001
1002 /* ######################################################################### */
1003
1004 #elif defined(MM_SHM_MALLOC)
1005
1006 #define MM_SHM_TYPE "malloc"
1007
1008 static void* mm_create_shm(const char* key, size_t size) {
1009   MM* p = (MM*)malloc(sizeof(MM));
1010   if (p == NULL) {
1011     return (MM*)-1;
1012   }
1013   p->size  = size;
1014   p->start = NULL;
1015   return p;
1016 }
1017
1018 static void mm_destroy_shm(MM* mm) {
1019   free(mm);
1020 }
1021
1022 /* ######################################################################### */
1023
1024 #else
1025 #define MM_SHM_TYPE "none"
1026 #  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"
1027 #endif
1028
1029 #ifdef MM_SHM_MALLOC
1030 static void mm_init(MM* mm) {
1031   mm->available = mm->size - sizeof(MM);
1032   mm->lock = malloc(sizeof(mm_mutex));
1033 }
1034
1035 void* mm_malloc_nolock(MM* mm, size_t size) {
1036   if (size > 0) {
1037     mm_mem_head *p = NULL;
1038     if (mm->available >= MM_SIZE(size)) {
1039       p = malloc(MM_SIZE(size));
1040       if (p != NULL) {
1041         p->size = MM_SIZE(size);
1042         mm->available -= MM_SIZE(size);
1043       }
1044     }
1045     if (p != NULL) {
1046       return HEAD_TO_PTR(p);
1047     }
1048   }
1049   return NULL;
1050 }
1051
1052 void mm_free_nolock(MM* mm, void* x) {
1053   if (x != NULL) {
1054     mm_mem_head *p;
1055     p = PTR_TO_HEAD(x);
1056     mm->available += p->size;
1057     free(p);
1058   }
1059 }
1060
1061 size_t mm_maxsize(MM* mm) {
1062   size_t ret;
1063   if (!mm_lock(mm, MM_LOCK_RD)) {
1064     return 0;
1065   }
1066   ret = mm->available - MM_SIZE(0);
1067   mm_unlock(mm);
1068   return ret;
1069 }
1070
1071 #else
1072 static void mm_init(MM* mm) {
1073   mm->start = MM_ALIGN(mm->start);
1074   mm->attach_addr = (void*)mm;
1075   mm->lock = mm->start;
1076   mm->start = MM_ALIGN((void*)(((char*)(mm->start)) + sizeof(mm_mutex)));
1077   mm->available = mm->size - (((char*)(mm->start))-(char*)mm);
1078   mm->free_list = (mm_free_bucket*)mm->start;
1079   mm->free_list->size = mm->available;
1080   mm->free_list->next = NULL;
1081 }
1082
1083 void* mm_malloc_nolock(MM* mm, size_t size) {
1084   if (size > 0) {
1085     mm_mem_head* x = NULL;
1086     size_t realsize = (size_t)MM_ALIGN(MM_SIZE(size));
1087     if (realsize <= mm->available) {
1088       /* Search for free bucket */
1089       mm_free_bucket* p = mm->free_list;
1090       mm_free_bucket* q = NULL;
1091       mm_free_bucket* best = NULL;
1092       mm_free_bucket* best_prev = NULL;
1093       while (p != NULL) {
1094         if (p->size == realsize) {
1095           /* Found free bucket with the same size */
1096           if (q == NULL) {
1097             mm->free_list = p->next;
1098             x = (mm_mem_head*)p;
1099           } else {
1100             q->next = p->next;
1101             x = (mm_mem_head*)p;
1102           }
1103           break;
1104         } else if (p->size > realsize && (best == NULL || best->size > p->size)) {
1105           /* Found best bucket (smallest bucket with the grater size) */
1106           best = p;
1107           best_prev = q;
1108         }
1109         q = p;
1110         p = p->next;
1111       }
1112       if (x == NULL && best != NULL) {
1113         if (best->size-realsize < sizeof(mm_free_bucket)) {
1114           realsize = best->size;
1115           x = (mm_mem_head*)best;
1116           if (best_prev == NULL) {
1117             mm->free_list = best->next;
1118           } else {
1119             best_prev->next = best->next;
1120           }
1121         } else {
1122           if (best_prev == NULL) {
1123             mm->free_list = (mm_free_bucket*)((char*)best + realsize);
1124             mm->free_list->size = best->size-realsize;
1125             mm->free_list->next = best->next;
1126           } else {
1127             best_prev->next = (mm_free_bucket*)((char*)best + realsize);
1128             best_prev->next->size = best->size-realsize;
1129             best_prev->next->next = best->next;
1130           }
1131           best->size = realsize;
1132           x = (mm_mem_head*)best;
1133         }
1134       }
1135       if (x != NULL) {
1136         mm->available -= realsize;
1137       }
1138     }
1139     if (x != NULL) {
1140       return HEAD_TO_PTR(x);
1141     }
1142   }
1143   return NULL;
1144 }
1145
1146 void mm_free_nolock(MM* mm, void* x) {
1147   if (x != NULL) {
1148     if (x >= mm->start && x < (void*)((char*)mm + mm->size)) {
1149       mm_mem_head *p = PTR_TO_HEAD(x);
1150       size_t size = p->size;
1151       if ((char*)p+size <= (char*)mm + mm->size) {
1152         mm_free_bucket* b = (mm_free_bucket*)p;
1153         b->next = NULL;
1154         if (mm->free_list == NULL) {
1155           mm->free_list = b;
1156         } else {
1157           mm_free_bucket* q = mm->free_list;
1158           mm_free_bucket* prev = NULL;
1159           mm_free_bucket* next = NULL;
1160           while (q != NULL) {
1161             if (b < q) {
1162               next = q;
1163               break;
1164             }
1165             prev = q;
1166             q = q->next;
1167           }
1168           if (prev != NULL && (char*)prev+prev->size == (char*)b) {
1169             if ((char*)next == (char*)b+size) {
1170               /* merging with prev and next */
1171               prev->size += size + next->size;
1172               prev->next = next->next;
1173             } else {
1174               /* merging with prev */
1175               prev->size += size;
1176             }
1177           } else {
1178             if ((char*)next == (char*)b+size) {
1179               /* merging with next */
1180               b->size += next->size;
1181               b->next = next->next;
1182             } else {
1183               /* don't merge */
1184               b->next = next;
1185             }
1186             if (prev != NULL) {
1187               prev->next = b;
1188             } else {
1189               mm->free_list = b;
1190             }
1191           }
1192         }
1193         mm->available += size;
1194       }
1195     }
1196   }
1197 }
1198
1199 size_t mm_maxsize(MM* mm) {
1200   size_t ret = MM_SIZE(0);
1201   mm_free_bucket* p;
1202   if (!mm_lock(mm, MM_LOCK_RD)) {
1203     return 0;
1204   }
1205   p = mm->free_list;
1206   while (p != NULL) {
1207     if (p->size > ret) {
1208       ret = p->size;
1209     }
1210     p = p->next;
1211   }
1212   mm_unlock(mm);
1213   return ret - MM_SIZE(0);
1214 }
1215 #endif
1216
1217 void* mm_malloc_lock(MM* mm, size_t size) {
1218   void *ret;
1219   if (!mm_lock(mm, MM_LOCK_RW)) {
1220     return NULL;
1221   }
1222   ret = mm_malloc_nolock(mm,size);
1223   mm_unlock(mm);
1224   return ret;
1225 }
1226
1227 void mm_free_lock(MM* mm, void* x) {
1228   mm_lock(mm, MM_LOCK_RW);
1229   mm_free_nolock(mm,x);
1230   mm_unlock(mm);
1231 }
1232
1233 void mm_set_attach(MM* mm, void* attach_addr) {
1234   mm->attach_addr = attach_addr;
1235 }
1236
1237 void* mm_attach(size_t size, const char* key) {
1238 #ifdef MM_SHM_CAN_ATTACH
1239   MM* mm = mm_attach_shm(key, size);
1240   if (mm == (MM*)-1) {
1241     return NULL;
1242   }
1243 #ifdef MM_SEM_CAN_ATTACH
1244   if (!mm_attach_lock(key, mm->lock)) {
1245     mm_destroy_shm(mm);
1246     return NULL;
1247   }
1248 #endif
1249   return mm->attach_addr;
1250 #else
1251   return NULL;
1252 #endif
1253 }
1254
1255 MM* mm_create(size_t size, const char* key) {
1256   MM* p;
1257   if (size == 0) {
1258     size = 32 * 1024 * 1024;
1259   }
1260   p = mm_create_shm(key, size);
1261   if (p == (MM*)-1) {
1262     return NULL;
1263   }
1264   mm_init(p);
1265   if (p->lock == NULL) {
1266     mm_destroy_shm(p);
1267     return NULL;
1268   }
1269   if (!mm_init_lock(key, p->lock)) {
1270     mm_destroy_shm(p);
1271     return NULL;
1272   }
1273   return p;
1274 }
1275
1276 void mm_destroy(MM* mm) {
1277   if (mm != NULL) {
1278     mm_destroy_lock(mm->lock);
1279     mm_destroy_shm(mm);
1280   }
1281 }
1282
1283 size_t mm_size(MM* mm) {
1284   if (mm != NULL) {
1285     return mm->size;
1286   }
1287   return 0;
1288 }
1289
1290 size_t mm_sizeof(MM* mm, void* x) {
1291   mm_mem_head *p;
1292   size_t ret;
1293   if (mm == NULL || x == NULL || !mm_lock(mm, MM_LOCK_RD)) {
1294     return 0;
1295   }
1296   p = PTR_TO_HEAD(x);
1297   ret = p->size;
1298   mm_unlock(mm);
1299   return ret;
1300 }
1301
1302 size_t mm_available(MM* mm) {
1303   size_t available;
1304   if (mm != NULL && mm_lock(mm, MM_LOCK_RD)) {
1305     available = mm->available;
1306     mm_unlock(mm);
1307     return available;
1308   }
1309   return 0;
1310 }
1311
1312 const char* mm_shm_type() {
1313   return MM_SHM_TYPE;
1314 }
1315
1316 const char* mm_sem_type() {
1317   return MM_SEM_TYPE;
1318 }
1319
1320 int mm_protect(MM* mm, int mode) {
1321 #ifdef HAVE_MPROTECT
1322   int pmode = 0;
1323   if (mode & MM_PROT_NONE) {
1324     pmode |= PROT_NONE;
1325   }
1326   if (mode & MM_PROT_READ) {
1327     pmode |= PROT_READ;
1328   }
1329   if (mode & MM_PROT_WRITE) {
1330     pmode |= PROT_WRITE;
1331   }
1332   if (mode & MM_PROT_EXEC) {
1333     pmode |= PROT_EXEC;
1334   }
1335   return (mprotect(mm, mm->size, pmode) == 0);
1336 #endif
1337   return 0;
1338 }
1339
1340 #ifdef MM_TEST_SHM
1341 int main() {
1342   char key[] = "/tmp/mm";
1343   size_t size = 32*1024*1024;
1344   MM *mm = mm_create(size, key);
1345   if (mm == NULL) {
1346     return 1;
1347   }
1348   mm_destroy(mm);
1349   return 0;
1350 }
1351 #endif
1352
1353 #ifdef MM