sleepy_penguin 3.5.2
[sleepy_penguin.git] / ext / sleepy_penguin / init.c
blob76fa77ad4fba989622bc5c8bf3aa43a3661db429
1 #include <ruby.h>
2 #ifndef _GNU_SOURCE
3 # define _GNU_SOURCE /* TODO: confirm this is needed */
4 #endif
6 #include <unistd.h>
7 #include <pthread.h>
8 #include <sys/types.h>
9 #include "git_version.h"
10 #include "sleepy_penguin.h"
11 #define L1_CACHE_LINE_MAX 128 /* largest I've seen (Pentium 4) */
12 size_t rb_sp_l1_cache_line_size;
13 static pthread_key_t rb_sp_key;
14 enum rb_sp_tls_buf_type {
15 RB_SP_TLS_INUSE = -1,
16 RB_SP_TLS_READY = 0,
17 RB_SP_TLS_MALLOCED = 1
20 struct rb_sp_tlsbuf {
21 uint32_t capa;
22 enum rb_sp_tls_buf_type buf_type;
23 unsigned char ptr[FLEX_ARRAY];
26 #ifdef HAVE_SYS_EVENT_H
27 void sleepy_penguin_init_kqueue(void);
28 #else
29 # define sleepy_penguin_init_kqueue() for(;0;)
30 #endif
32 #ifdef HAVE_SYS_EPOLL_H
33 void sleepy_penguin_init_epoll(void);
34 #else
35 # define sleepy_penguin_init_epoll() for(;0;)
36 #endif
38 #ifdef HAVE_SYS_TIMERFD_H
39 void sleepy_penguin_init_timerfd(void);
40 #else
41 # define sleepy_penguin_init_timerfd() for(;0;)
42 #endif
44 #ifdef HAVE_SYS_EVENTFD_H
45 void sleepy_penguin_init_eventfd(void);
46 #else
47 # define sleepy_penguin_init_eventfd() for(;0;)
48 #endif
50 #ifdef HAVE_SYS_INOTIFY_H
51 void sleepy_penguin_init_inotify(void);
52 #else
53 # define sleepy_penguin_init_inotify() for(;0;)
54 #endif
56 #ifdef HAVE_SYS_SIGNALFD_H
57 void sleepy_penguin_init_signalfd(void);
58 #else
59 # define sleepy_penguin_init_signalfd() for(;0;)
60 #endif
62 #ifdef HAVE_SPLICE
63 void sleepy_penguin_init_splice(void);
64 #else
65 # define sleepy_penguin_init_splice() for(;0;)
66 #endif
68 #if defined(HAVE_COPY_FILE_RANGE) || \
69 (defined(__linux__) && defined(__NR_copy_file_range))
70 void sleepy_penguin_init_cfr(void);
71 #else
72 # define sleepy_penguin_init_cfr() for (;0;)
73 #endif
75 /* everyone */
76 void sleepy_penguin_init_sendfile(void);
78 static size_t l1_cache_line_size_detect(void)
80 #ifdef _SC_LEVEL1_DCACHE_LINESIZE
81 long tmp = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
83 if (tmp > 0 && tmp <= L1_CACHE_LINE_MAX)
84 return (size_t)tmp;
85 #endif /* _SC_LEVEL1_DCACHE_LINESIZE */
86 return L1_CACHE_LINE_MAX;
89 static void sp_once(void)
91 int err = pthread_key_create(&rb_sp_key, free);
93 if (err) {
94 errno = err;
95 rb_sys_fail( "pthread_key_create");
99 static struct rb_sp_tlsbuf *alloc_tlsbuf(size_t size)
101 size_t bytes = size + sizeof(struct rb_sp_tlsbuf);
102 struct rb_sp_tlsbuf *buf;
103 void *ptr;
105 if (size >= UINT32_MAX ||
106 posix_memalign(&ptr, rb_sp_l1_cache_line_size, bytes))
107 rb_memerror(); /* fatal */
109 buf = ptr;
110 buf->capa = (uint32_t)size;
112 return buf;
115 void *rb_sp_gettlsbuf(size_t *size)
117 struct rb_sp_tlsbuf *buf = pthread_getspecific(rb_sp_key);
118 int err;
120 assert(buf ? buf->buf_type != RB_SP_TLS_MALLOCED : 1);
122 if (buf && buf->buf_type != RB_SP_TLS_READY) {
123 buf = alloc_tlsbuf(*size);
124 buf->buf_type = RB_SP_TLS_MALLOCED;
125 return buf->ptr;
128 if (buf && buf->capa >= *size) {
129 *size = buf->capa;
130 goto out;
133 free(buf);
134 buf = alloc_tlsbuf(*size);
135 err = pthread_setspecific(rb_sp_key, buf);
136 if (err != 0) {
137 free(buf);
138 errno = err;
139 rb_sys_fail("BUG: pthread_setspecific");
141 out:
142 buf->buf_type = RB_SP_TLS_INUSE;
143 return buf->ptr;
146 #define container_of(ptr, type, member) \
147 (type *)((uintptr_t)(ptr) - offsetof(type, member))
149 VALUE rb_sp_puttlsbuf(VALUE p)
151 struct rb_sp_tlsbuf *tls = pthread_getspecific(rb_sp_key);
152 void *ptr = (void *)p;
153 struct rb_sp_tlsbuf *buf;
155 if (!ptr)
156 return Qfalse;
158 buf = container_of(ptr, struct rb_sp_tlsbuf, ptr);
160 switch (buf->buf_type) {
161 case RB_SP_TLS_INUSE:
162 assert(tls == buf && "rb_sp_puttlsbuf mismatch");
163 buf->buf_type = RB_SP_TLS_READY;
164 break;
165 case RB_SP_TLS_READY:
166 assert(0 && "rb_sp_gettlsbuf not called");
167 case RB_SP_TLS_MALLOCED:
168 free(buf);
170 return Qfalse;
173 void Init_sleepy_penguin_ext(void)
175 VALUE mSleepyPenguin;
176 static pthread_once_t once = PTHREAD_ONCE_INIT;
177 int err = pthread_once(&once, sp_once);
179 if (err) {
180 errno = err;
181 rb_sys_fail("pthread_once");
184 rb_sp_l1_cache_line_size = l1_cache_line_size_detect();
186 mSleepyPenguin = rb_define_module("SleepyPenguin");
187 rb_define_const(mSleepyPenguin, "SLEEPY_PENGUIN_VERSION",
188 rb_str_new2(MY_GIT_VERSION));
190 sleepy_penguin_init_kqueue();
191 sleepy_penguin_init_epoll();
192 sleepy_penguin_init_timerfd();
193 sleepy_penguin_init_eventfd();
194 sleepy_penguin_init_inotify();
195 sleepy_penguin_init_signalfd();
196 sleepy_penguin_init_splice();
197 sleepy_penguin_init_cfr();
198 sleepy_penguin_init_sendfile();