1 /*
2 +--------------------------------------------------------------------+
3 | ext-fiber |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Authors: Aaron Piotrowski <aaron@trowski.com> |
10 | Martin Schröder <m.schroeder2007@gmail.com> |
11 +--------------------------------------------------------------------+
12 */
13
14 #ifdef HAVE_CONFIG_H
15 # include "config.h"
16 #endif
17
18 #include "fiber.h"
19
20 #ifdef HAVE_VALGRIND
21 # include <valgrind/valgrind.h>
22 #endif
23
24 #ifndef PHP_WIN32
25 # include <unistd.h>
26 # include <sys/mman.h>
27 # include <limits.h>
28 #endif
29
30 #define ZEND_FIBER_DEFAULT_PAGE_SIZE 4096
31
32 /*
33 * FreeBSD require a first (i.e. addr) argument of mmap(2) is not NULL
34 * if MAP_STACK is passed.
35 * http://www.FreeBSD.org/cgi/query-pr.cgi?pr=158755
36 */
37 #if defined(MAP_STACK) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)
38 # define ZEND_FIBER_STACK_FLAGS (MAP_PRIVATE | MAP_ANON | MAP_STACK)
39 #else
40 # define ZEND_FIBER_STACK_FLAGS (MAP_PRIVATE | MAP_ANON)
41 #endif
42
zend_fiber_page_size()43 static size_t zend_fiber_page_size()
44 {
45 #if _POSIX_MAPPED_FILES
46 static size_t page_size;
47
48 if (!page_size) {
49 page_size = sysconf(_SC_PAGESIZE);
50 }
51
52 return page_size;
53 #else
54 return ZEND_FIBER_DEFAULT_PAGE_SIZE;
55 #endif
56 }
57
zend_fiber_stack_allocate(zend_fiber_stack * stack,size_t size)58 bool zend_fiber_stack_allocate(zend_fiber_stack *stack, size_t size)
59 {
60 void *pointer;
61 const size_t page_size = zend_fiber_page_size();
62
63 ZEND_ASSERT(size >= page_size + ZEND_FIBER_GUARD_PAGES * page_size);
64
65 stack->size = (size + page_size - 1) / page_size * page_size;
66 const size_t msize = stack->size + ZEND_FIBER_GUARD_PAGES * page_size;
67
68 #ifdef PHP_WIN32
69 pointer = VirtualAlloc(0, msize, MEM_COMMIT, PAGE_READWRITE);
70
71 if (!pointer) {
72 return false;
73 }
74
75 # if ZEND_FIBER_GUARD_PAGES
76 DWORD protect;
77
78 if (!VirtualProtect(pointer, ZEND_FIBER_GUARD_PAGES * page_size, PAGE_READWRITE | PAGE_GUARD, &protect)) {
79 VirtualFree(pointer, 0, MEM_RELEASE);
80 return false;
81 }
82 # endif
83 #else
84 pointer = mmap(NULL, msize, PROT_READ | PROT_WRITE, ZEND_FIBER_STACK_FLAGS, -1, 0);
85
86 if (pointer == MAP_FAILED) {
87 return false;
88 }
89
90 # if ZEND_FIBER_GUARD_PAGES
91 if (mprotect(pointer, ZEND_FIBER_GUARD_PAGES * page_size, PROT_NONE) < 0) {
92 munmap(pointer, msize);
93 return false;
94 }
95 # endif
96 #endif
97
98 stack->pointer = (void *) ((uintptr_t) pointer + ZEND_FIBER_GUARD_PAGES * page_size);
99
100 #ifdef VALGRIND_STACK_REGISTER
101 uintptr_t base = (uintptr_t) stack->pointer;
102 stack->valgrind = VALGRIND_STACK_REGISTER(base, base + stack->size);
103 #endif
104
105 return true;
106 }
107
zend_fiber_stack_free(zend_fiber_stack * stack)108 void zend_fiber_stack_free(zend_fiber_stack *stack)
109 {
110 if (!stack->pointer) {
111 return;
112 }
113
114 #ifdef VALGRIND_STACK_DEREGISTER
115 VALGRIND_STACK_DEREGISTER(stack->valgrind);
116 #endif
117
118 const size_t page_size = zend_fiber_page_size();
119
120 void *pointer = (void *) ((uintptr_t) stack->pointer - ZEND_FIBER_GUARD_PAGES * page_size);
121
122 #ifdef PHP_WIN32
123 VirtualFree(pointer, 0, MEM_RELEASE);
124 #else
125 munmap(pointer, stack->size + ZEND_FIBER_GUARD_PAGES * page_size);
126 #endif
127
128 stack->pointer = NULL;
129 }
130
131 /*
132 * vim: sw=4 ts=4
133 * vim600: fdm=marker
134 */
135