xref: /ext-fiber/src/fiber_stack.c (revision 676413a5)
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