root/eaccelerator/branches/0.9.5/mm.c

Revision 257, 30.1 kB (checked in by bart, 2 years ago)

Fixed detection of SysV IPC shared memory and made anonymous mmap

the first selected type. This makes the behaviour of rc1 the
default. anonymous mmap doens't have the problem of shmmax. SysVIPC is
still available!

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