├── .gitignore
├── LICENSE
├── README.rst
├── setup.py
└── src
└── c10k
├── C10kPthread.pyx
├── C10kPthread.setup
├── C10kSocket.pyx
├── C10kSocket.setup
├── Makefile
├── __init__.py
├── __main__.py
├── _gevent_cgreenlet.h
├── libc10k.c
├── libc10k.setup
└── pthread.pxd
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies of this
6 | license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates the
10 | terms and conditions of version 3 of the GNU General Public License,
11 | supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License, other than
20 | an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided by the
23 | Library, but which is not otherwise based on the Library. Defining a
24 | subclass of a class defined by the Library is deemed a mode of using an
25 | interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an Application
28 | with the Library. The particular version of the Library with which the
29 | Combined Work was made is also called the "Linked Version".
30 |
31 | The "Minimal Corresponding Source" for a Combined Work means the
32 | Corresponding Source for the Combined Work, excluding any source code for
33 | portions of the Combined Work that, considered in isolation, are based on
34 | the Application, and not on the Linked Version.
35 |
36 | The "Corresponding Application Code" for a Combined Work means the object
37 | code and/or source code for the Application, including any data and utility
38 | programs needed for reproducing the Combined Work from the Application, but
39 | excluding the System Libraries of the Combined Work.
40 |
41 | 1. Exception to Section 3 of the GNU GPL.
42 |
43 | You may convey a covered work under sections 3 and 4 of this License
44 | without being bound by section 3 of the GNU GPL.
45 |
46 | 2. Conveying Modified Versions.
47 |
48 | If you modify a copy of the Library, and, in your modifications, a facility
49 | refers to a function or data to be supplied by an Application that uses the
50 | facility (other than as an argument passed when the facility is invoked),
51 | then you may convey a copy of the modified version:
52 |
53 | a) under this License, provided that you make a good faith effort to
54 | ensure that, in the event an Application does not supply the function
55 | or data, the facility still operates, and performs whatever part of its
56 | purpose remains meaningful, or
57 |
58 | b) under the GNU GPL, with none of the additional permissions of this
59 | License applicable to that copy.
60 |
61 | 3. Object Code Incorporating Material from Library Header Files.
62 |
63 | The object code form of an Application may incorporate material from a
64 | header file that is part of the Library. You may convey such object code
65 | under terms of your choice, provided that, if the incorporated material is
66 | not limited to numerical parameters, data structure layouts and accessors,
67 | or small macros, inline functions and templates (ten or fewer lines in
68 | length), you do both of the following:
69 |
70 | a) Give prominent notice with each copy of the object code that the
71 | Library is used in it and that the Library and its use are covered by
72 | this License.
73 |
74 | b) Accompany the object code with a copy of the GNU GPL and this
75 | license document.
76 |
77 | 4. Combined Works.
78 |
79 | You may convey a Combined Work under terms of your choice that, taken
80 | together, effectively do not restrict modification of the portions of the
81 | Library contained in the Combined Work and reverse engineering for
82 | debugging such modifications, if you also do each of the following:
83 |
84 | a) Give prominent notice with each copy of the Combined Work that the
85 | Library is used in it and that the Library and its use are covered by
86 | this License.
87 |
88 | b) Accompany the Combined Work with a copy of the GNU GPL and this
89 | license document.
90 |
91 | c) For a Combined Work that displays copyright notices during
92 | execution, include the copyright notice for the Library among these
93 | notices, as well as a reference directing the user to the copies of the
94 | GNU GPL and this license document.
95 |
96 | d) Do one of the following:
97 |
98 | 0) Convey the Minimal Corresponding Source under the terms of this
99 | License, and the Corresponding Application Code in a form suitable
100 | for, and under terms that permit, the user to recombine or relink
101 | the Application with a modified version of the Linked Version to
102 | produce a modified Combined Work, in the manner specified by
103 | section 6 of the GNU GPL for conveying Corresponding Source.
104 |
105 | 1) Use a suitable shared library mechanism for linking with the
106 | Library. A suitable mechanism is one that (a) uses at run time
107 | a copy of the Library already present on the user's computer
108 | system, and (b) will operate properly with a modified version of
109 | the Library that is interface-compatible with the Linked Version.
110 |
111 | e) Provide Installation Information, but only if you would otherwise
112 | be required to provide such information under section 6 of the GNU GPL,
113 | and only to the extent that such information is necessary to install
114 | and execute a modified version of the Combined Work produced by
115 | recombining or relinking the Application with a modified version of the
116 | Linked Version. (If you use option 4d0, the Installation Information
117 | must accompany the Minimal Corresponding Source and Corresponding
118 | Application Code. If you use option 4d1, you must provide the
119 | Installation Information in the manner specified by section 6 of the
120 | GNU GPL for conveying Corresponding Source.)
121 |
122 | 5. Combined Libraries.
123 |
124 | You may place library facilities that are a work based on the Library side
125 | by side in a single library together with other library facilities that are
126 | not Applications and are not covered by this License, and convey such a
127 | combined library under terms of your choice, if you do both of the
128 | following:
129 |
130 | a) Accompany the combined library with a copy of the same work based on
131 | the Library, uncombined with any other library facilities, conveyed
132 | under the terms of this License.
133 |
134 | b) Give prominent notice with the combined library that part of it is a
135 | work based on the Library, and explaining where to find the
136 | accompanying uncombined form of the same work.
137 |
138 | 6. Revised Versions of the GNU Lesser General Public License.
139 |
140 | The Free Software Foundation may publish revised and/or new versions of the
141 | GNU Lesser General Public License from time to time. Such new versions will
142 | be similar in spirit to the present version, but may differ in detail to
143 | address new problems or concerns.
144 |
145 | Each version is given a distinguishing version number. If the Library as
146 | you received it specifies that a certain numbered version of the GNU Lesser
147 | General Public License "or any later version" applies to it, you have the
148 | option of following the terms and conditions either of that published
149 | version or of any later version published by the Free Software Foundation.
150 | If the Library as you received it does not specify a version number of the
151 | GNU Lesser General Public License, you may choose any version of the GNU
152 | Lesser General Public License ever published by the Free Software
153 | Foundation.
154 |
155 | If the Library as you received it specifies that a proxy can decide whether
156 | future versions of the GNU Lesser General Public License shall apply, that
157 | proxy's public statement of acceptance of any version is permanent
158 | authorization for you to choose that version for the Library.
159 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | =========
2 | C10K plan
3 | =========
4 |
5 | Introduction
6 | ------------
7 |
8 | C10K is a coroutine-based alternative implementation of the posix thread.
9 | It allows existing non-asynchronous programs, regardless of the programming
10 | language they use, to become coroutine-based asynchronous programs without
11 | any modifications, and makes it possible for programs to handle a large
12 | number of clients at the same time.
13 |
14 | At this moment C10K is still under heavy development.
15 |
16 |
17 | Usage
18 | -----
19 |
20 | The **c10k** command can start a specified program that runs
21 | asynchronously.
22 |
23 | .. code-block:: console
24 |
25 | $ c10k ab -c 1000 -n 10000 http://example.com/
26 |
27 | .. code-block:: console
28 |
29 | $ c10k bash
30 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import re
4 | import setuptools
5 |
6 | with open('src/c10k/__init__.py') as f:
7 | version = re.search(r"__version__\s*=\s*'(.*)'", f.read()).group(1)
8 | with open('README.rst') as f:
9 | readme = f.read()
10 |
11 | setuptools.setup(
12 | name='c10k',
13 | version=version,
14 | description='',
15 | long_description=readme,
16 | license='LGPLv3',
17 | keywords='',
18 | author='Wilhelm Shen',
19 | author_email='wilhelmshen@pyforce.com',
20 | url='http://c10k.pyforce.com',
21 | package_dir={'': 'src'},
22 | packages=['c10k'],
23 | install_requires=
24 | [
25 | 'gevent>=20.4.0'
26 | ]
27 | classifiers=
28 | [
29 | 'License :: OSI Approved :: GNU Lesser General Public '+
30 | 'License v3 (LGPLv3)',
31 | 'Programming Language :: Python :: 3.6',
32 | 'Programming Language :: Python :: 3.7',
33 | 'Programming Language :: Python :: 3.8',
34 | 'Programming Language :: Python :: 3.9',
35 | 'Programming Language :: Python :: Implementation :: '+
36 | 'CPython',
37 | 'Operating System :: POSIX',
38 | 'Topic :: Internet',
39 | 'Topic :: Software Development :: Libraries :: '+
40 | 'Python Modules',
41 | 'Intended Audience :: Developers',
42 | 'Development Status :: 1 - Planning'
43 | ],
44 | python_requires='>=3.6',
45 | entry_points=
46 | {
47 | 'console_scripts': ['c10k=c10k.__main__:main']
48 | }
49 | )
50 |
--------------------------------------------------------------------------------
/src/c10k/C10kPthread.pyx:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | # cython: language_level=3
3 |
4 | cimport cpython.mem
5 | cimport cpython.object
6 | cimport cpython.pycapsule
7 | cimport cpython.ref
8 | cimport cython
9 | cimport libc.errno
10 | cimport libc.stdint
11 | cimport libc.stdio
12 | cimport libc.stdlib
13 | cimport posix.time
14 | cimport posix.types
15 |
16 | from pthread cimport *
17 |
18 | cdef extern from '':
19 |
20 | enum:
21 | EDEADLK, # 35 Resource deadlock would occur
22 | # for robust mutexes
23 | EOWNERDEAD # 130 Owner died
24 |
25 | cdef extern from '':
26 |
27 | cpython.object.PyObject* _PyObject_CallMethod \
28 | 'PyObject_CallMethod' \
29 | (
30 | cpython.object.PyObject *,
31 | char *,
32 | ...
33 | ) nogil
34 |
35 | cdef extern from '':
36 |
37 | ctypedef struct PyGreenlet:
38 |
39 | char* stack_start
40 | PyGreenlet* parent
41 | cpython.object.PyObject* run_info
42 |
43 | ctypedef class greenlet.greenlet [object PyGreenlet]:
44 |
45 | cdef greenlet parent
46 | cdef object run_info
47 |
48 | greenlet PyGreenlet_GetCurrent()
49 |
50 | cdef extern from '_gevent_cgreenlet.h':
51 |
52 | ctypedef struct PyC10kGeventGreenlet:
53 |
54 | PyGreenlet __pyx_base
55 | void * __pyx_vtab
56 | cpython.object.PyObject *value
57 | cpython.object.PyObject *args
58 | cpython.object.PyObject *kwargs
59 | cpython.object.PyObject *spawning_greenlet
60 | cpython.object.PyObject *spawning_stack
61 | cpython.object.PyObject *spawn_tree_locals
62 | cpython.object.PyObject *_links
63 | cpython.object.PyObject *_exc_info
64 | cpython.object.PyObject *_notifier
65 | cpython.object.PyObject *_start_event
66 | cpython.object.PyObject *_formatted_info
67 | cpython.object.PyObject *_ident
68 |
69 | ctypedef class gevent._greenlet.Greenlet [object PyC10kGeventGreenlet]:
70 |
71 | cdef object value
72 | cdef object args
73 | cdef object kwargs
74 | cdef object spawning_greenlet
75 | cdef object spawning_stack
76 | cdef object spawn_tree_locals
77 | cdef object _links
78 | cdef object _exc_info
79 | cdef object _notifier
80 | cdef object _start_event
81 | cdef object _formatted_info
82 | cdef object _ident
83 |
84 | cdef extern from 'C10kPthread.h':
85 |
86 | cpython.object.PyObject* __pyx_empty_tuple
87 | cpython.object.PyObject* __Pyx_PyObject_CallOneArg \
88 | (
89 | cpython.object.PyObject *,
90 | cpython.object.PyObject *
91 | )
92 |
93 | cdef public int C10kPthread_initialized = 0
94 | cdef int __concurrency_level = 0
95 | cdef void *__key_limbo_specific
96 | cdef pthread_key_t __key_limbo = 0
97 | cdef pthread_once_t ONCE_DONE = -1
98 | cdef pthread_t TH_MAIN = 0
99 |
100 | ###########################################################################
101 |
102 | import gevent
103 | import gevent._greenlet
104 | import gevent.event
105 | import gevent.local
106 | import gevent.lock
107 | import gevent.os
108 | import gevent.queue
109 | import sys
110 | import time
111 | import weakref
112 |
113 | from gevent._greenlet import Greenlet
114 |
115 | dummy_event_finder = gevent.Greenlet(lambda: None)
116 | dummy_event_finder.throw(gevent.GreenletExit)
117 | _cancelled_start_event = (dummy_event_finder)._start_event
118 | dummy_event_finder = gevent.Greenlet(lambda: None)
119 | dummy_event_finder.start()
120 | dummy_event_finder.join()
121 | _start_completed_event = (dummy_event_finder)._start_event
122 | del dummy_event_finder
123 | g_main = gevent.getcurrent()
124 | greenlets = \
125 | {
126 | TH_MAIN : g_main,
127 | g_main: g_main
128 | }
129 | mutexes = {}
130 | rwlocks = {}
131 | conds = {}
132 | barriers = {}
133 | atfork_prepare = []
134 | atfork_parent = []
135 | atfork_child = []
136 | __local = gevent.local.local()
137 | LOCAL_KEY = 'C10kPthreadKey'
138 | sys.setswitchinterval(0xffffffff)
139 |
140 | ###########################################################################
141 |
142 | cdef class Attr:
143 |
144 | cdef int detachstate
145 | cdef size_t guardsize
146 | cdef sched_param schedparam
147 | cdef int schedpolicy
148 | cdef int inheritsched
149 | cdef int scope
150 | cdef void *stackaddr
151 | cdef size_t stacksize
152 |
153 | cdef int init(self, const pthread_attr_t *attr):
154 | if NULL == attr:
155 | return self.set_default()
156 | else:
157 | return self.set(attr)
158 |
159 | cdef int set_default(self):
160 | cdef pthread_attr_t attr
161 | cdef int fail = pthread_attr_init(&attr)
162 | if fail:
163 | return fail
164 | return self.set(&attr)
165 |
166 | cdef int set(self, const pthread_attr_t *attr):
167 | cdef int fail
168 | fail = pthread_attr_getdetachstate(attr, &self.detachstate)
169 | if fail:
170 | return fail
171 | fail = pthread_attr_getguardsize(attr, &self.guardsize)
172 | if fail:
173 | return fail
174 | fail = pthread_attr_getschedparam(attr, &self.schedparam)
175 | if fail:
176 | return fail
177 | fail = pthread_attr_getschedpolicy(attr, &self.schedpolicy)
178 | if fail:
179 | return fail
180 | fail = pthread_attr_getinheritsched(attr, &self.inheritsched)
181 | if fail:
182 | return fail
183 | fail = pthread_attr_getscope(attr, &self.scope)
184 | if fail:
185 | return fail
186 | fail = \
187 | pthread_attr_getstack(
188 | attr,
189 | &self.stackaddr,
190 | &self.stacksize
191 | )
192 | if fail:
193 | return fail
194 | return 0
195 |
196 | cdef int get(self, pthread_attr_t *attr):
197 | cdef int fail
198 | fail = pthread_attr_setdetachstate(attr, self.detachstate)
199 | if fail:
200 | return fail
201 | fail = pthread_attr_setguardsize(attr, self.guardsize)
202 | if fail:
203 | return fail
204 | fail = pthread_attr_setschedparam(attr, &self.schedparam)
205 | if fail != 0:
206 | return fail
207 | fail = pthread_attr_setschedpolicy(attr, self.schedpolicy)
208 | if fail != 0:
209 | return fail
210 | fail = pthread_attr_setinheritsched(attr, self.inheritsched)
211 | if fail != 0:
212 | return fail
213 | fail = pthread_attr_setscope(attr, self.scope)
214 | if fail != 0:
215 | return fail
216 | fail = pthread_attr_setstack(attr, self.stackaddr, self.stacksize)
217 | if fail:
218 | return fail
219 | return 0
220 |
221 | cdef class Thread:
222 |
223 | cdef bytes name
224 | cdef Attr attr
225 | cdef int cancelstate
226 | cdef int canceltype
227 | cdef void *(*start_routine) (void *) nogil
228 | cdef void *arg
229 | cdef void *retval
230 |
231 | cdef int init \
232 | (
233 | self,
234 | bytes name,
235 | Attr attr,
236 | void *(*start_routine) (void *) nogil,
237 | void *arg
238 | ):
239 | self.name = name
240 | self.attr = attr
241 | self.cancelstate = PTHREAD_CANCEL_DISABLE
242 | self.canceltype = PTHREAD_CANCEL_DEFERRED
243 | self.start_routine = start_routine
244 | self.arg = arg
245 | self.retval = NULL
246 | return 0
247 |
248 | def run():
249 | g = gevent.getcurrent()
250 | if g._start_event is None:
251 | g._start_event = _cancelled_start_event
252 | g._start_event.stop()
253 | g._start_event.close()
254 | g._start_event = _start_completed_event
255 | t = g.args[0]
256 | cdef void *start_routine = t.start_routine
257 | cdef void *arg = t.arg
258 | del t
259 | del g
260 | cdef void *retval = (start_routine)(arg)
261 | g = gevent.getcurrent()
262 | t = g.args[0]
263 | key = \
264 | \
265 | \
266 | g
267 | t.retval = retval
268 | g._exc_info = (None, None, None)
269 | g.value = None
270 | if g._links and not g._notifier:
271 | g._notifier = (g) \
272 | . parent \
273 | . loop \
274 | . run_callback(g._notify_links)
275 | del g.run
276 | g.args = ()
277 | g.kwargs.clear()
278 | del greenlets[key]
279 |
280 | # Create a new thread, starting with execution of START-ROUTINE
281 | # getting passed ARG. Creation attributed come from ATTR. The new
282 | # handle is stored in *NEWTHREAD.
283 | cdef public int pthread_create \
284 | (
285 | pthread_t *newthread,
286 | const pthread_attr_t *attr,
287 | void *(*start_routine) (void *) nogil,
288 | void *arg
289 | ):
290 | pAttr = Attr()
291 | cdef int fail = pAttr.init(attr)
292 | if fail:
293 | return fail
294 | t = Thread()
295 | g = Greenlet()
296 | t.init(g.name.encode(), pAttr, start_routine, arg)
297 | g.run = run
298 | g.args = (t, )
299 | g.start()
300 | key = \
301 | \
302 | \
303 | g
304 | greenlets[key] = g
305 | newthread[0] = \
306 | \
307 | \
308 | g
309 | return 0
310 |
311 | # Terminate calling thread.
312 | #
313 | # The registered cleanup handlers are called via exception handling
314 | # so we cannot mark this function with __THROW.
315 | cdef public void pthread_exit(void *retval):
316 | g = gevent.getcurrent()
317 | cdef pthread_t th = \
318 | \
319 | \
320 | g
321 | del g
322 | __cancel(th, retval)
323 |
324 | cdef void __cancel(pthread_t th, void *retval):
325 | key = th
326 | g = greenlets[key]
327 | t = g.args[0]
328 | if retval != NULL:
329 | t.retval = retval
330 | g._exc_info = (None, None, None)
331 | g.value = None
332 | if g._links and not g._notifier:
333 | g._notifier = (g) \
334 | . parent \
335 | . loop \
336 | . run_callback(g._notify_links)
337 | g.args = ()
338 | g.kwargs.clear()
339 | del g.run
340 | del greenlets[key]
341 | del t
342 | del key
343 | cdef cpython.object.PyObject *pMyHub = \
344 | (g).parent
345 | (g).stack_start = NULL
346 | del g
347 | # XXX
348 | _PyObject_CallMethod(pMyHub, b'switch', NULL)
349 |
350 | # Make calling thread wait for termination of the thread TH. The
351 | # exit status of the thread is stored in *THREAD_RETURN, if THREAD_RETURN
352 | # is not NULL.
353 | #
354 | # This function is a cancellation point and therefore not marked with
355 | # __THROW.
356 | cdef public int pthread_join(pthread_t th, void **thread_return):
357 | key = th
358 | try:
359 | g = greenlets[key]
360 | except KeyError:
361 | return libc.errno.ESRCH
362 | try:
363 | t = g.args[0]
364 | except TypeError:
365 | return libc.errno.ESRCH
366 | if t.attr.detachstate != PTHREAD_CREATE_JOINABLE:
367 | return libc.errno.EINVAL
368 | g.join()
369 | if thread_return != NULL:
370 | thread_return = &t.retval
371 | return 0
372 |
373 | # Check whether thread TH has terminated. If yes return the status of
374 | # the thread in *THREAD_RETURN, if THREAD_RETURN is not NULL.
375 | cdef public int pthread_tryjoin_np(pthread_t th, void **thread_return):
376 | key = th
377 | try:
378 | g = greenlets[key]
379 | except KeyError:
380 | return libc.errno.ESRCH
381 | try:
382 | t = g.args[0]
383 | except TypeError:
384 | return libc.errno.ESRCH
385 | if t.attr.detachstate != PTHREAD_CREATE_JOINABLE:
386 | return libc.errno.EINVAL
387 | if g.ready():
388 | if thread_return != NULL:
389 | thread_return = &t.retval
390 | return 0
391 | else:
392 | return libc.errno.EBUSY
393 |
394 | # Make calling thread wait for termination of the thread TH, but only
395 | # until TIMEOUT. The exit status of the thread is stored in
396 | # *THREAD_RETURN, if THREAD_RETURN is not NULL.
397 | #
398 | # This function is a cancellation point and therefore not marked with
399 | # __THROW.
400 | cdef public int pthread_timedjoin_np \
401 | (
402 | pthread_t th,
403 | void **thread_return,
404 | const posix.time.timespec *abstime
405 | ):
406 | key = th
407 | try:
408 | g = greenlets[key]
409 | except KeyError:
410 | return libc.errno.ESRCH
411 | try:
412 | t = g.args[0]
413 | except TypeError:
414 | return libc.errno.ESRCH
415 | if t.attr.detachstate != PTHREAD_CREATE_JOINABLE:
416 | return libc.errno.EINVAL
417 | g.join(timeout=max(abstime.tv_sec-int(time.time()), 0))
418 | if g.ready():
419 | if thread_return != NULL:
420 | thread_return = &t.retval
421 | return 0
422 | else:
423 | return libc.errno.ETIMEDOUT
424 |
425 | # Indicate that the thread TH is never to be joined with PTHREAD_JOIN.
426 | # The resources of TH will therefore be freed immediately when it
427 | # terminates, instead of waiting for another thread to perform PTHREAD_JOIN
428 | # on it.
429 | cdef public int pthread_detach(pthread_t th):
430 | key = th
431 | try:
432 | g = greenlets[key]
433 | except KeyError:
434 | return libc.errno.ESRCH
435 | try:
436 | t = g.args[0]
437 | except TypeError:
438 | return libc.errno.ESRCH
439 | t.attr.detachstate = PTHREAD_CREATE_DETACHED
440 | return 0
441 |
442 | # Obtain the identifier of the current thread.
443 | cdef public pthread_t pthread_self():
444 | cdef pthread_t th
445 | if C10kPthread_initialized:
446 | g = gevent.getcurrent()
447 | if g is g_main:
448 | return TH_MAIN
449 | else:
450 | th = \
451 | \
452 | \
453 | g
454 | return th
455 | else:
456 | return TH_MAIN
457 |
458 | # Compare two thread identifiers.
459 | cdef public int pthread_equal(pthread_t thread1, pthread_t thread2):
460 | key1 = thread1
461 | key2 = thread2
462 | if key1 == key2:
463 | if key1 in greenlets:
464 | return 1
465 | else:
466 | return libc.errno.ESRCH
467 | else:
468 | if key1 not in greenlets:
469 | return libc.errno.ESRCH
470 | elif key2 not in greenlets:
471 | return libc.errno.ESRCH
472 | else:
473 | return 0
474 |
475 | ###########################################################################
476 | # Thread attribute handling. #
477 | ###########################################################################
478 |
479 | # Initialize thread attribute *ATTR with attributes corresponding to the
480 | # already running thread TH. It shall be called on uninitialized ATTR
481 | # and destroyed with pthread_attr_destroy when no longer needed.
482 | cdef public int pthread_getattr_np(pthread_t th, pthread_attr_t *attr):
483 | key = th
484 | try:
485 | g = greenlets[key]
486 | except KeyError:
487 | return libc.errno.ESRCH
488 | try:
489 | t = g.args[0]
490 | except TypeError:
491 | return libc.errno.ESRCH
492 | return t.attr.get(attr)
493 |
494 | ###########################################################################
495 | # Functions for scheduling control. #
496 | ###########################################################################
497 |
498 | # Set the scheduling parameters for TARGET_THREAD according to POLICY
499 | # and *PARAM.
500 | cdef public int pthread_setschedparam \
501 | (
502 | pthread_t target_thread,
503 | int policy,
504 | const sched_param *param
505 | ):
506 | key = target_thread
507 | try:
508 | g = greenlets[key]
509 | except KeyError:
510 | return libc.errno.ESRCH
511 | try:
512 | t = g.args[0]
513 | except TypeError:
514 | return libc.errno.ESRCH
515 | t.schedpolicy = policy
516 | t.schedparam.sched_priority = param.sched_priority
517 | return 0
518 |
519 | # Return in *POLICY and *PARAM the scheduling parameters for TARGET_THREAD.
520 | cdef public int pthread_getschedparam \
521 | (
522 | pthread_t target_thread,
523 | int *policy,
524 | sched_param *param
525 | ):
526 | key = target_thread
527 | try:
528 | g = greenlets[key]
529 | except KeyError:
530 | return libc.errno.ESRCH
531 | try:
532 | t = g.args[0]
533 | except TypeError:
534 | return libc.errno.ESRCH
535 | policy[0] = t.schedpolicy
536 | param.sched_priority = t.schedparam.sched_priority
537 | return 0
538 |
539 | # Set the scheduling priority for TARGET_THREAD.
540 | cdef public int pthread_setschedprio(pthread_t target_thread, int prio):
541 | key = target_thread
542 | try:
543 | g = greenlets[key]
544 | except KeyError:
545 | return libc.errno.ESRCH
546 | try:
547 | t = g.args[0]
548 | except TypeError:
549 | return libc.errno.ESRCH
550 | t.schedparam.sched_priority = prio
551 | return 0
552 |
553 | # Get thread name visible in the kernel and its interfaces.
554 | cdef public int pthread_getname_np \
555 | (
556 | pthread_t target_thread,
557 | char *buf,
558 | size_t buflen
559 | ):
560 | key = target_thread
561 | try:
562 | g = greenlets[key]
563 | except KeyError:
564 | return libc.errno.ESRCH
565 | try:
566 | t = g.args[0]
567 | except TypeError:
568 | return libc.errno.ESRCH
569 | if len(t.name) > buflen:
570 | return libc.errno.ERANGE
571 | name = t.name
572 | return 0
573 |
574 | # Set thread name visible in the kernel and its interfaces.
575 | cdef public int pthread_setname_np \
576 | (
577 | pthread_t target_thread,
578 | const char *name
579 | ):
580 | key = target_thread
581 | try:
582 | g = greenlets[key]
583 | except KeyError:
584 | return libc.errno.ESRCH
585 | try:
586 | t = g.args[0]
587 | except TypeError:
588 | return libc.errno.ESRCH
589 | t.name = name
590 | return 0
591 |
592 | # Determine level of concurrency.
593 | cdef public int pthread_getconcurrency():
594 | return __concurrency_level
595 |
596 | # Set new concurrency level to LEVEL.
597 | cdef public int pthread_setconcurrency(int level):
598 | if level < 0:
599 | return libc.errno.EINVAL
600 | global __concurrency_level
601 | __concurrency_level = level
602 | return 0
603 |
604 | # Yield the processor to another thread or process.
605 | # This function is similar to the POSIX `sched_yield' function but
606 | # might be differently implemented in the case of a m-on-n thread
607 | # implementation.
608 | cdef public int pthread_yield():
609 | gevent.idle()
610 | return 0
611 |
612 | # XXX
613 | #
614 | # Limit specified thread TH to run only on the processors represented
615 | # in CPUSET.
616 | cdef public int pthread_setaffinity_np \
617 | (
618 | pthread_t th,
619 | size_t cpusetsize,
620 | const cpu_set_t *cpuset
621 | ):
622 | return libc.errno.ENOSYS
623 |
624 | # XXX
625 | #
626 | # Get bit set in CPUSET representing the processors TH can run on.
627 | cdef public int pthread_getaffinity_np \
628 | (
629 | pthread_t th,
630 | size_t cpusetsize,
631 | cpu_set_t *cpuset
632 | ):
633 | return libc.errno.ENOSYS
634 |
635 | ###########################################################################
636 | # Functions for handling initialization. #
637 | ###########################################################################
638 |
639 | # Guarantee that the initialization function INIT_ROUTINE will be called
640 | # only once, even if pthread_once is executed several times with the
641 | # same ONCE_CONTROL argument. ONCE_CONTROL must point to a static or
642 | # extern variable initialized to PTHREAD_ONCE_INIT.
643 | #
644 | # The initialization functions might throw exception which is why
645 | # this function is not marked with __THROW.
646 | cdef public int pthread_once \
647 | (
648 | pthread_once_t *once_control,
649 | void (*init_routine) ()
650 | ):
651 | if PTHREAD_ONCE_INIT == once_control[0]:
652 | once_control[0] = ONCE_DONE
653 | init_routine()
654 | return 0
655 | else:
656 | return 0
657 |
658 | ###########################################################################
659 | # Functions for handling cancellation. #
660 | #
661 | # Note that these functions are explicitly not marked to not throw an #
662 | # exception in C++ code. If cancellation is implemented by unwinding #
663 | # this is necessary to have the compiler generate the unwind information. #
664 | ###########################################################################
665 |
666 | # Set cancelability state of current thread to STATE, returning old
667 | # state in *OLDSTATE if OLDSTATE is not NULL.
668 | cdef public int pthread_setcancelstate(int state, int *oldstate):
669 | g = gevent.getcurrent()
670 | key = g
671 | if key not in greenlets:
672 | return libc.errno.ESRCH
673 | try:
674 | t = g.args[0]
675 | except TypeError:
676 | return libc.errno.ESRCH
677 | oldstate[0] = t.cancelstate
678 | t.cancelstate = state
679 | return 0
680 |
681 | # Set cancellation state of current thread to TYPE, returning the old
682 | # type in *OLDTYPE if OLDTYPE is not NULL.
683 | cdef public int pthread_setcanceltype(int type_, int *oldtype):
684 | g = gevent.getcurrent()
685 | key = g
686 | try:
687 | t = g.args[0]
688 | except TypeError:
689 | return libc.errno.ESRCH
690 | oldtype[0] = t.canceltype
691 | t.canceltype = type_
692 | return 0
693 |
694 | # Cancel THREAD immediately or at the next possibility.
695 | cdef public int pthread_cancel(pthread_t th):
696 | key = th
697 | try:
698 | g = greenlets[key]
699 | except KeyError:
700 | return libc.errno.ESRCH
701 | try:
702 | t = g.args[0]
703 | except TypeError:
704 | return libc.errno.ESRCH
705 | t.cancelstate = PTHREAD_CANCEL_ENABLE
706 | del t
707 | del g
708 | del key
709 | __cancel(th, PTHREAD_CANCELED)
710 | return 0
711 |
712 | # Test for pending cancellation for the current thread and terminate
713 | # the thread as per pthread_exit(PTHREAD_CANCELED) if it has been
714 | # cancelled.
715 | cdef public void pthread_testcancel():
716 | g = gevent.getcurrent()
717 | key = g
718 | if key not in greenlets:
719 | return
720 | cdef pthread_t th = g
721 | try:
722 | t = g.args[0]
723 | except TypeError:
724 | return
725 | if PTHREAD_CANCEL_ENABLE != t.cancelstate:
726 | return
727 | del t
728 | del key
729 | del g
730 | __cancel(th, PTHREAD_CANCELED)
731 |
732 | # XXX
733 | cdef public int pthread_kill(pthread_t th, int sig):
734 | key = th
735 | try:
736 | g = greenlets[key]
737 | except KeyError:
738 | return libc.errno.ESRCH
739 | return libc.errno.ENOSYS
740 |
741 | ###########################################################################
742 | # Mutex handling. #
743 | ###########################################################################
744 |
745 | cdef class MutexAttr:
746 |
747 | cdef int pshared
748 | cdef int kind
749 | cdef int protocol
750 | cdef int prioceiling
751 | cdef int robustness
752 |
753 | cdef int init(self, const pthread_mutexattr_t *attr):
754 | if NULL == attr:
755 | return self.set_default()
756 | else:
757 | return self.set(attr)
758 |
759 | cdef int set_default(self):
760 | cdef pthread_mutexattr_t attr
761 | cdef int fail
762 | fail = pthread_mutexattr_init(&attr)
763 | if fail:
764 | return fail
765 | return self.set(&attr)
766 |
767 | cdef int set(self, const pthread_mutexattr_t *attr):
768 | cdef int fail = pthread_mutexattr_getpshared(attr, &self.pshared)
769 | if fail:
770 | return fail
771 | fail = pthread_mutexattr_gettype(attr, &self.kind)
772 | if fail:
773 | return fail
774 | fail = pthread_mutexattr_getprotocol(attr, &self.protocol)
775 | if fail:
776 | return fail
777 | fail = \
778 | pthread_mutexattr_getprioceiling(
779 | attr,
780 | &self.prioceiling
781 | )
782 | if fail:
783 | return fail
784 | fail = pthread_mutexattr_getrobust(attr, &self.robustness)
785 | if fail:
786 | return fail
787 | return 0
788 |
789 | cdef int get(self, pthread_mutexattr_t *attr):
790 | cdef int fail = pthread_mutexattr_setpshared(attr, self.pshared)
791 | if fail:
792 | return fail
793 | fail = pthread_mutexattr_settype(attr, self.kind)
794 | if fail:
795 | return fail
796 | fail = pthread_mutexattr_setprotocol(attr, self.protocol)
797 | if fail:
798 | return fail
799 | fail = pthread_mutexattr_setprioceiling(attr, self.prioceiling)
800 | if fail:
801 | return fail
802 | fail = pthread_mutexattr_setrobust(attr, self.robustness)
803 | if fail:
804 | return fail
805 | return 0
806 |
807 | cdef class Mutex:
808 |
809 | cdef MutexAttr attr
810 | cdef object _block
811 | cdef object _owner
812 | cdef object _count
813 | cdef object __weakref__
814 |
815 | def __init__(self):
816 | self._block = gevent.lock.Semaphore(1)
817 | self._owner = None
818 | self._count = 0
819 |
820 | cdef int acquire(self, blocking, timeout):
821 | if self._owner is None:
822 | owner = None
823 | else:
824 | owner = self._owner()
825 | if owner is None or owner.ready():
826 | return EOWNERDEAD
827 | me = gevent.getcurrent()
828 | if owner is me:
829 | if self.attr.kind == PTHREAD_MUTEX_RECURSIVE_NP:
830 | self._count = self._count + 1
831 | return 0
832 | elif self.attr.kind == PTHREAD_MUTEX_ERRORCHECK_NP:
833 | return EDEADLK
834 | rc = self._block.acquire(blocking, timeout)
835 | if rc:
836 | self._owner = weakref.ref(me)
837 | self._count = 1
838 | return 0
839 | else:
840 | if timeout is None:
841 | return libc.errno.EBUSY
842 | else:
843 | return libc.errno.ETIMEDOUT
844 |
845 | cdef int release(self):
846 | if self._owner is None:
847 | owner = None
848 | else:
849 | owner = self._owner()
850 | me = gevent.getcurrent()
851 | attr = self.attr
852 | if owner is not me and \
853 | (
854 | attr.kind == PTHREAD_MUTEX_ERRORCHECK or \
855 | attr.kind == PTHREAD_MUTEX_RECURSIVE or \
856 | attr.robustness
857 | ):
858 | return libc.errno.EPERM
859 | count = self._count - 1
860 | self._count = count
861 | if not count:
862 | self._owner = None
863 | self._block.release()
864 | return 0
865 |
866 | cdef int cond_wait_check(self):
867 | if PTHREAD_MUTEX_ERRORCHECK == self.attr.kind or \
868 | self.attr.robustness:
869 | if self._owner is None:
870 | return EOWNERDEAD
871 | else:
872 | owner = self._owner()
873 | if owner is None:
874 | return EOWNERDEAD
875 | me = gevent.getcurrent()
876 | if owner is not me:
877 | return libc.errno.EPERM
878 | return 0
879 |
880 | # Initialize a mutex.
881 | cdef public int pthread_mutex_init \
882 | (
883 | pthread_mutex_t *mutex,
884 | const pthread_mutexattr_t *mutexattr
885 | ):
886 | if not C10kPthread_initialized:
887 | (mutex)[0] = 0
888 | return 0
889 | pAttr = MutexAttr()
890 | cdef int fail = pAttr.init(mutexattr)
891 | if fail:
892 | return fail
893 | pMutex = Mutex()
894 | pMutex.attr = pAttr
895 | key = \
896 | \
897 | \
898 | pMutex
899 | mutexes[key] = pMutex
900 | (mutex)[0] = \
901 | \
902 | pMutex
903 | return 0
904 |
905 | # Destroy a mutex.
906 | cdef public int pthread_mutex_destroy(pthread_mutex_t *mutex):
907 | if 0 == (mutex)[0]:
908 | return 0
909 | key = (mutex)[0]
910 | try:
911 | del mutexes[key]
912 | except KeyError:
913 | return libc.errno.ESRCH
914 | else:
915 | return 0
916 |
917 | # Try locking a mutex.
918 | cdef public int pthread_mutex_trylock(pthread_mutex_t *mutex):
919 | key = (mutex)[0]
920 | try:
921 | pMutex = mutexes[key]
922 | except KeyError:
923 | return libc.errno.ESRCH
924 | return pMutex.acquire(False, None)
925 |
926 | # Lock a mutex.
927 | cdef public int pthread_mutex_lock(pthread_mutex_t *mutex):
928 | if 0 == (mutex)[0]:
929 | return 0
930 | key = (mutex)[0]
931 | try:
932 | pMutex = mutexes[key]
933 | except KeyError:
934 | return libc.errno.ESRCH
935 | return pMutex.acquire(True, None)
936 |
937 | # Wait until lock becomes available, or specified time passes.
938 | cdef public int pthread_mutex_timedlock \
939 | (
940 | pthread_mutex_t *mutex,
941 | const posix.time.timespec *abstime
942 | ):
943 | key = (mutex)[0]
944 | try:
945 | pMutex = mutexes[key]
946 | except KeyError:
947 | return libc.errno.ESRCH
948 | return \
949 | pMutex.acquire(
950 | True,
951 | max(abstime.tv_sec-int(time.time()), 0)
952 | )
953 |
954 | # Unlock a mutex.
955 | cdef public int pthread_mutex_unlock(pthread_mutex_t *mutex):
956 | if 0 == (mutex)[0]:
957 | return 0
958 | key = (mutex)[0]
959 | try:
960 | pMutex = mutexes[key]
961 | except KeyError:
962 | return libc.errno.ESRCH
963 | return pMutex.release()
964 |
965 | # Get the priority ceiling of MUTEX.
966 | cdef public int pthread_mutex_getprioceiling \
967 | (
968 | const pthread_mutex_t *mutex,
969 | int *prioceiling
970 | ):
971 | key = (mutex)[0]
972 | try:
973 | pMutex = mutexes[key]
974 | except KeyError:
975 | return libc.errno.ESRCH
976 | prioceiling[0] = pMutex.attr.prioceiling
977 |
978 | # XXX
979 | #
980 | # Set the priority ceiling of MUTEX to PRIOCEILING, return old
981 | # priority ceiling value in *OLD_CEILING.
982 | cdef public int pthread_mutex_setprioceiling \
983 | (
984 | pthread_mutex_t *mutex,
985 | int prioceiling,
986 | int *old_ceiling
987 | ):
988 | key = (mutex)[0]
989 | try:
990 | pMutex = mutexes[key]
991 | except KeyError:
992 | return libc.errno.ESRCH
993 | g = gevent.getcurrent()
994 | key = g
995 | try:
996 | g = greenlets[key]
997 | except KeyError:
998 | return libc.errno.ESRCH
999 | try:
1000 | t = g.args[0]
1001 | except TypeError:
1002 | return libc.errno.ESRCH
1003 | attr = pMutex.attr
1004 | if attr.protocol == PTHREAD_PRIO_NONE:
1005 | return libc.errno.EINVAL
1006 | elif attr.protocol == PTHREAD_PRIO_PROTECT and \
1007 | t.schedparam.sched_priority > attr.prioceiling:
1008 | return libc.errno.EINVAL
1009 | cdef int fail = pMutex.acquire(True, None)
1010 | if fail:
1011 | return fail
1012 | old_ceiling[0] = pMutex.attr.prioceiling
1013 | pMutex.attr.prioceiling = prioceiling
1014 | pMutex.release()
1015 | return 0
1016 |
1017 | # XXX
1018 | #
1019 | # Declare the state protected by MUTEX as consistent.
1020 | cdef public int pthread_mutex_consistent(pthread_mutex_t *mutex):
1021 | key = (mutex)[0]
1022 | try:
1023 | pMutex = mutexes[key]
1024 | except KeyError:
1025 | return libc.errno.ESRCH
1026 | if pMutex.attr.robustness:
1027 | return 0
1028 | else:
1029 | return libc.errno.EINVAL
1030 |
1031 | cdef public int pthread_mutex_consistent_np(pthread_mutex_t *mutex):
1032 | return pthread_mutex_consistent(mutex)
1033 |
1034 | ###########################################################################
1035 | # Functions for handling read-write locks. #
1036 | ###########################################################################
1037 |
1038 | cdef class RwlockAttr:
1039 |
1040 | cdef int pshared
1041 | cdef int pref
1042 |
1043 | cdef int init(self, const pthread_rwlockattr_t *attr):
1044 | if NULL == attr:
1045 | return self.set_default()
1046 | else:
1047 | return self.set(attr)
1048 |
1049 | cdef int set_default(self):
1050 | cdef pthread_rwlockattr_t attr
1051 | cdef int fail = pthread_rwlockattr_init(&attr)
1052 | if fail:
1053 | return fail
1054 | return self.set(&attr)
1055 |
1056 | cdef int set(self, const pthread_rwlockattr_t *attr):
1057 | cdef int fail = pthread_rwlockattr_getpshared(attr, &self.pshared)
1058 | if fail:
1059 | return fail
1060 | fail = pthread_rwlockattr_getkind_np(attr, &self.pref)
1061 | if fail:
1062 | return fail
1063 | return 0
1064 |
1065 | cdef int get(self, pthread_rwlockattr_t *attr):
1066 | cdef int fail = pthread_rwlockattr_setpshared(attr, self.pshared)
1067 | if fail:
1068 | return fail
1069 | fail = pthread_rwlockattr_setkind_np(attr, self.pref)
1070 | if fail:
1071 | return fail
1072 | return 0
1073 |
1074 | cdef class Rwlock_PREFER_READER:
1075 |
1076 | cdef RwlockAttr attr
1077 | cdef int balance
1078 | cdef object _block
1079 | cdef object _ready
1080 | cdef dict _owner
1081 | cdef object __weakref__
1082 |
1083 | def __init__(self):
1084 | self.balance = 0
1085 | self._block = gevent.lock.Semaphore(1)
1086 | self._ready = gevent.event.Event()
1087 | self._owner = {}
1088 |
1089 | cdef int rd_acquire(self, blocking=True, timeout=None):
1090 | g = gevent.getcurrent()
1091 | key = g
1092 | if key in self._owner:
1093 | return EDEADLK
1094 | if self.balance > 0:
1095 | self.balance += 1
1096 | self._owner[key] = 0
1097 | return 0
1098 | elif 0 == self.balance:
1099 | self.balance = -1
1100 | rc = self._block.acquire(blocking, timeout)
1101 | if rc:
1102 | self.balance = abs(self.balance)
1103 | self._ready.set()
1104 | self._owner[key] = 0
1105 | return 0
1106 | else:
1107 | if timeout is None:
1108 | return libc.errno.EBUSY
1109 | else:
1110 | return libc.errno.ETIMEDOUT
1111 | else:
1112 | self.balance -= 1
1113 | rc = self._ready.wait(timeout)
1114 | if rc:
1115 | self._owner[key] = 0
1116 | return 0
1117 | else:
1118 | return libc.errno.ETIMEDOUT
1119 |
1120 | cdef int wr_acquire(self, blocking, timeout):
1121 | g = gevent.getcurrent()
1122 | key = g
1123 | if key in self._owner:
1124 | return EDEADLK
1125 | rc = self._block.acquire(blocking, timeout)
1126 | if rc:
1127 | self._owner[key] = 1
1128 | return 0
1129 | else:
1130 | if timeout is None:
1131 | return libc.errno.EBUSY
1132 | else:
1133 | return libc.errno.ETIMEDOUT
1134 |
1135 | cdef int release(self):
1136 | g = gevent.getcurrent()
1137 | key = g
1138 | is_writer = self._owner.get(key)
1139 | if key is None:
1140 | return libc.errno.EPERM
1141 | if key:
1142 | self._block.release()
1143 | del self._owner[key]
1144 | return 0
1145 | else:
1146 | self.balance -= 1
1147 | if 0 == self.balance:
1148 | self._ready.clear()
1149 | self._block.release()
1150 | del self._owner[key]
1151 | return 0
1152 |
1153 | # Initialize read-write lock RWLOCK using attributes ATTR, or use
1154 | # the default values if later is NULL.
1155 | cdef public int pthread_rwlock_init \
1156 | (
1157 | pthread_rwlock_t *rwlock,
1158 | const pthread_rwlockattr_t *attr
1159 | ):
1160 | pAttr = RwlockAttr()
1161 | cdef int fail = pAttr.init(attr)
1162 | if fail:
1163 | return fail
1164 | pRwlock = Rwlock_PREFER_READER()
1165 | pRwlock.attr = pAttr
1166 | key = \
1167 | \
1168 | \
1169 | pRwlock
1170 | rwlocks[key] = pRwlock
1171 | (rwlock)[0] = \
1172 | \
1173 | pRwlock
1174 | return 0
1175 |
1176 | # Destroy read-write lock RWLOCK.
1177 | cdef public int pthread_rwlock_destroy(pthread_rwlock_t *rwlock):
1178 | key = (rwlock)[0]
1179 | try:
1180 | del rwlocks[key]
1181 | except KeyError:
1182 | return libc.errno.ESRCH
1183 | else:
1184 | return 0
1185 |
1186 | # Acquire read lock for RWLOCK.
1187 | cdef public int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock):
1188 | key = (rwlock)[0]
1189 | try:
1190 | pRwlock = rwlocks[key]
1191 | except KeyError:
1192 | return libc.errno.ESRCH
1193 | return pRwlock.rd_acquire(True, None)
1194 |
1195 | # Try to acquire read lock for RWLOCK.
1196 | cdef public int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock):
1197 | key = (rwlock)[0]
1198 | try:
1199 | pRwlock = rwlocks[key]
1200 | except KeyError:
1201 | return libc.errno.ESRCH
1202 | return pRwlock.rd_acquire(False, None)
1203 |
1204 | # Try to acquire read lock for RWLOCK or return after specfied time.
1205 | cdef public int pthread_rwlock_timedrdlock \
1206 | (
1207 | pthread_rwlock_t *rwlock,
1208 | const posix.time.timespec *abstime
1209 | ):
1210 | key = (rwlock)[0]
1211 | try:
1212 | pRwlock = rwlocks[key]
1213 | except KeyError:
1214 | return libc.errno.ESRCH
1215 | return \
1216 | pRwlock.rd_acquire(
1217 | True,
1218 | max(abstime.tv_sec-int(time.time()), 0)
1219 | )
1220 |
1221 | # Acquire write lock for RWLOCK.
1222 | cdef public int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock):
1223 | key = (rwlock)[0]
1224 | try:
1225 | pRwlock = rwlocks[key]
1226 | except KeyError:
1227 | return libc.errno.ESRCH
1228 | return pRwlock.wr_acquire(True, None)
1229 |
1230 | # Try to acquire write lock for RWLOCK.
1231 | cdef public int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock):
1232 | key = (rwlock)[0]
1233 | try:
1234 | pRwlock = rwlocks[key]
1235 | except KeyError:
1236 | return libc.errno.ESRCH
1237 | return pRwlock.wr_acquire(False, None)
1238 |
1239 | # Try to acquire write lock for RWLOCK or return after specfied time.
1240 | cdef public int pthread_rwlock_timedwrlock \
1241 | (
1242 | pthread_rwlock_t *rwlock,
1243 | const posix.time.timespec *abstime
1244 | ):
1245 | key = (rwlock)[0]
1246 | try:
1247 | pRwlock = rwlocks[key]
1248 | except KeyError:
1249 | return libc.errno.ESRCH
1250 | return \
1251 | pRwlock.wr_acquire(
1252 | True,
1253 | max(abstime.tv_sec-int(time.time()), 0)
1254 | )
1255 |
1256 | # Unlock RWLOCK.
1257 | cdef public int pthread_rwlock_unlock(pthread_rwlock_t *rwlock):
1258 | key = (rwlock)[0]
1259 | try:
1260 | pRwlock = rwlocks[key]
1261 | except KeyError:
1262 | return libc.errno.ESRCH
1263 | return pRwlock.release()
1264 |
1265 | ###########################################################################
1266 | # Functions for handling conditional variables. #
1267 | ###########################################################################
1268 |
1269 | cdef class CondAttr:
1270 |
1271 | cdef int pshared
1272 | cdef __clockid_t clock_id
1273 |
1274 | cdef int init(self, const pthread_condattr_t *attr):
1275 | if NULL == attr:
1276 | return self.set_default()
1277 | else:
1278 | return self.set(attr)
1279 |
1280 | cdef int set_default(self):
1281 | cdef pthread_condattr_t attr
1282 | cdef int fail = pthread_condattr_init(&attr)
1283 | if fail:
1284 | return fail
1285 | return self.set(&attr)
1286 |
1287 | cdef int set(self, const pthread_condattr_t *attr):
1288 | cdef int fail = pthread_condattr_getpshared(attr, &self.pshared)
1289 | if fail:
1290 | return fail
1291 | fail = pthread_condattr_getclock(attr, &self.clock_id)
1292 | if fail != 0:
1293 | return fail
1294 | return 0
1295 |
1296 | cdef int get(self, pthread_condattr_t *attr):
1297 | cdef int fail = pthread_condattr_setpshared(attr, self.pshared)
1298 | if fail:
1299 | return fail
1300 | fail = pthread_condattr_setclock(attr, self.clock_id)
1301 | if fail:
1302 | return fail
1303 | return 0
1304 |
1305 | cdef class Cond:
1306 |
1307 | cdef CondAttr attr
1308 | cdef public object queue
1309 |
1310 | def __init__(self):
1311 | self.queue = gevent.queue.Queue()
1312 |
1313 | cdef int init(self, CondAttr attr):
1314 | self.attr = attr
1315 | return 0
1316 |
1317 | # Initialize condition variable COND using attributes ATTR, or use
1318 | # the default values if later is NULL.
1319 | cdef public int pthread_cond_init \
1320 | (
1321 | pthread_cond_t *cond,
1322 | const pthread_condattr_t *cond_attr
1323 | ):
1324 | if not C10kPthread_initialized:
1325 | (cond)[0] = 0
1326 | return 0
1327 | pAttr = CondAttr()
1328 | cdef int fail = pAttr.init(cond_attr)
1329 | if fail:
1330 | return fail
1331 | pCond = Cond()
1332 | pCond.attr = pAttr
1333 | key = \
1334 | \
1335 | \
1336 | pCond
1337 | conds[key] = pCond
1338 | (cond)[0] = \
1339 | \
1340 | pCond
1341 | return 0
1342 |
1343 | # Destroy condition variable COND.
1344 | cdef public int pthread_cond_destroy(pthread_cond_t *cond):
1345 | key = (cond)[0]
1346 | try:
1347 | del conds[key]
1348 | except KeyError:
1349 | return libc.errno.ESRCH
1350 | else:
1351 | return 0
1352 |
1353 | # Wake up one thread waiting for condition variable COND.
1354 | cdef public int pthread_cond_signal(pthread_cond_t *cond):
1355 | if 0 == (cond)[0]:
1356 | return 0
1357 | key = (cond)[0]
1358 | try:
1359 | pCond = conds[key]
1360 | except KeyError:
1361 | return libc.errno.ESRCH
1362 | queue = pCond.queue
1363 | if len(queue.getters) > 0:
1364 | queue.put(None)
1365 | return 0
1366 | else:
1367 | return 0
1368 |
1369 | # Wake up all threads waiting for condition variables COND.
1370 | cdef public int pthread_cond_broadcast(pthread_cond_t *cond):
1371 | key = (cond)[0]
1372 | try:
1373 | pCond = conds[key]
1374 | except KeyError:
1375 | return libc.errno.ESRCH
1376 | queue = pCond.queue
1377 | if len(queue.getters) > 0:
1378 | pCond.queue = gevent.queue.Queue()
1379 | while len(queue.getters) > 0:
1380 | queue.put(None)
1381 | return 0
1382 | else:
1383 | return 0
1384 |
1385 | # Wait for condition variable COND to be signaled or broadcast.
1386 | # MUTEX is assumed to be locked before.
1387 | #
1388 | # This function is a cancellation point and therefore not marked with
1389 | # __THROW.
1390 | cdef public int pthread_cond_wait \
1391 | (
1392 | pthread_cond_t *cond,
1393 | pthread_mutex_t *mutex
1394 | ):
1395 | if 0 == (cond)[0]:
1396 | return 0
1397 | key = (mutex)[0]
1398 | try:
1399 | pMutex = mutexes[key]
1400 | except KeyError:
1401 | return libc.errno.ESRCH
1402 | fail = pMutex.cond_wait_check()
1403 | if fail:
1404 | return fail
1405 | key = (cond)[0]
1406 | try:
1407 | pCond = conds[key]
1408 | except KeyError:
1409 | return libc.errno.ESRCH
1410 | pCond.queue.get(block=True)
1411 | return 0
1412 |
1413 | # Wait for condition variable COND to be signaled or broadcast until
1414 | # ABSTIME. MUTEX is assumed to be locked before. ABSTIME is an
1415 | # absolute time specification; zero is the beginning of the epoch
1416 | # (00:00:00 GMT, January 1, 1970).
1417 | #
1418 | # This function is a cancellation point and therefore not marked with
1419 | # __THROW.
1420 | cdef public int pthread_cond_timedwait \
1421 | (
1422 | pthread_cond_t *cond,
1423 | pthread_mutex_t *mutex,
1424 | const posix.time.timespec *abstime
1425 | ):
1426 | key = (mutex)[0]
1427 | try:
1428 | pMutex = mutexes[key]
1429 | except KeyError:
1430 | return libc.errno.ESRCH
1431 | fail = pMutex.cond_wait_check()
1432 | if fail:
1433 | return fail
1434 | key = (cond)[0]
1435 | try:
1436 | pCond = conds[key]
1437 | except KeyError:
1438 | return libc.errno.ESRCH
1439 | pCond.queue.get(
1440 | block=True,
1441 | timeout=max(abstime.tv_sec-int(time.time()), 0)
1442 | )
1443 | return 0
1444 |
1445 | ###########################################################################
1446 | # Functions to handle spinlocks. #
1447 | ###########################################################################
1448 |
1449 | # Initialize the spinlock LOCK. If PSHARED is nonzero the spinlock can
1450 | # be shared between different processes.
1451 | cdef public int pthread_spin_init(pthread_spinlock_t *lock, int pshared):
1452 | pAttr = MutexAttr()
1453 | cdef int fail = pAttr.init(NULL)
1454 | pAttr.pshared = pshared
1455 | if fail:
1456 | return fail
1457 | pMutex = Mutex()
1458 | pMutex.attr = pAttr
1459 | key = \
1460 | \
1461 | \
1462 | pMutex
1463 | mutexes[key] = pMutex
1464 | (lock)[0] = \
1465 | \
1466 | pMutex
1467 | return 0
1468 |
1469 | # Destroy the spinlock LOCK.
1470 | cdef public int pthread_spin_destroy(pthread_spinlock_t *lock):
1471 | key = (lock)[0]
1472 | try:
1473 | del mutexes[key]
1474 | except KeyError:
1475 | return libc.errno.ESRCH
1476 | else:
1477 | return 0
1478 |
1479 | # Wait until spinlock LOCK is retrieved.
1480 | cdef public int pthread_spin_lock(pthread_spinlock_t *lock):
1481 | if 0 == (lock)[0]:
1482 | return 0
1483 | key = (lock)[0]
1484 | try:
1485 | pMutex = mutexes[key]
1486 | except KeyError:
1487 | return libc.errno.ESRCH
1488 | cdef int rc = pMutex.acquire(False, None)
1489 | while libc.errno.EBUSY == rc:
1490 | rc = pMutex.acquire(False, None)
1491 | return rc
1492 |
1493 | # Try to lock spinlock LOCK.
1494 | cdef public int pthread_spin_trylock(pthread_spinlock_t *lock):
1495 | key = (lock)[0]
1496 | try:
1497 | pMutex = mutexes[key]
1498 | except KeyError:
1499 | return libc.errno.ESRCH
1500 | return pMutex.acquire(False, None)
1501 |
1502 | # Release spinlock LOCK.
1503 | cdef public int pthread_spin_unlock(pthread_spinlock_t *lock):
1504 | key = (lock)[0]
1505 | try:
1506 | pMutex = mutexes[key]
1507 | except KeyError:
1508 | return libc.errno.ESRCH
1509 | return pMutex.release()
1510 |
1511 | ###########################################################################
1512 | # Functions to handle barriers. #
1513 | ###########################################################################
1514 |
1515 | cdef class BarrierAttr:
1516 |
1517 | cdef int pshared
1518 |
1519 | cdef int init(self, const pthread_barrierattr_t *attr):
1520 | if NULL == attr:
1521 | return self.set_default()
1522 | else:
1523 | return self.set(attr)
1524 |
1525 | cdef int set_default(self):
1526 | cdef pthread_barrierattr_t attr
1527 | cdef int fail = pthread_barrierattr_init(&attr)
1528 | if fail:
1529 | return fail
1530 | return self.set(&attr)
1531 |
1532 | cdef int set(self, const pthread_barrierattr_t *attr):
1533 | return pthread_barrierattr_getpshared(attr, &self.pshared)
1534 |
1535 | cdef int get(self, pthread_barrierattr_t *attr):
1536 | return pthread_barrierattr_setpshared(attr, self.pshared)
1537 |
1538 | cdef class Barrier:
1539 |
1540 | cdef BarrierAttr attr
1541 | cdef int count
1542 | cdef int waiters
1543 | cdef object ready
1544 |
1545 | cdef int init(self, BarrierAttr attr):
1546 | self.attr = attr
1547 | return 0
1548 |
1549 | # Initialize BARRIER with the attributes in ATTR. The barrier is
1550 | # opened when COUNT waiters arrived.
1551 | cdef public int pthread_barrier_init \
1552 | (
1553 | pthread_barrier_t *barrier,
1554 | const pthread_barrierattr_t *attr,
1555 | unsigned int count
1556 | ):
1557 | pAttr = BarrierAttr()
1558 | cdef int fail = pAttr.init(attr)
1559 | if fail:
1560 | return fail
1561 | pBarrier = Barrier()
1562 | pBarrier.attr = pAttr
1563 | pBarrier.count = count
1564 | pBarrier.waiters = 0
1565 | pBarrier.ready = gevent.event.Event()
1566 | key = \
1567 | \
1568 | \
1569 | pBarrier
1570 | barriers[key] = pBarrier
1571 | (barrier)[0] = \
1572 | \
1573 | pBarrier
1574 | return 0
1575 |
1576 | # Destroy a previously dynamically initialized barrier BARRIER.
1577 | cdef public int pthread_barrier_destroy(pthread_barrier_t *barrier):
1578 | key = (barrier)[0]
1579 | try:
1580 | del barriers[key]
1581 | except KeyError:
1582 | return libc.errno.ESRCH
1583 | else:
1584 | return 0
1585 |
1586 | # Wait on barrier BARRIER.
1587 | cdef public int pthread_barrier_wait(pthread_barrier_t *barrier):
1588 | key = (barrier)[0]
1589 | try:
1590 | pBarrier = barriers[key]
1591 | except KeyError:
1592 | return libc.errno.ESRCH
1593 | cdef waiters = pBarrier.waiters + 1
1594 | if waiters >= pBarrier.count:
1595 | pBarrier.waiters = 0
1596 | pBarrier.ready.set()
1597 | return 0
1598 | else:
1599 | pBarrier.waiters = waiters
1600 | pBarrier.ready.wait()
1601 | return 0
1602 |
1603 | ###########################################################################
1604 | # Functions for handling thread-specific data. #
1605 | ###########################################################################
1606 |
1607 | cdef class Key:
1608 |
1609 | cdef void *specific
1610 | cdef (void (*) (void *) nogil) destr_function
1611 |
1612 | def __dealloc__(self):
1613 | if self.specific != NULL and self.destr_function != NULL:
1614 | self.destr_function(self.specific)
1615 |
1616 | # Create a key value identifying a location in the thread-specific
1617 | # data area. Each thread maintains a distinct thread-specific data
1618 | # area. DESTR_FUNCTION, if non-NULL, is called with the value
1619 | # associated to that key when the key is destroyed.
1620 | # DESTR_FUNCTION is not called if the value associated is NULL when
1621 | # the key is destroyed.
1622 | cdef public int pthread_key_create \
1623 | (
1624 | pthread_key_t *key,
1625 | void (*destr_function) (void *) nogil
1626 | ):
1627 | global __key_limbo_specific
1628 | if not C10kPthread_initialized:
1629 | __key_limbo_specific = NULL
1630 | key[0] = __key_limbo
1631 | return 0
1632 | if LOCAL_KEY not in __local.__dict__:
1633 | __local.__dict__[LOCAL_KEY] = {}
1634 | pKey = Key()
1635 | pKey.specific = NULL
1636 | pKey.destr_function = destr_function
1637 | id_ = \
1638 | \
1639 | \
1640 | pKey
1641 | __local.__dict__[LOCAL_KEY][id_] = pKey
1642 | key[0] = \
1643 | \
1644 | \
1645 | pKey
1646 | return 0
1647 |
1648 | # Destroy KEY.
1649 | cdef public int pthread_key_delete(pthread_key_t key):
1650 | if LOCAL_KEY not in __local.__dict__:
1651 | return libc.errno.ESRCH
1652 | id_ = key
1653 | try:
1654 | del __local.__dict__[id_]
1655 | except KeyError:
1656 | return libc.errno.ESRCH
1657 | return 0
1658 |
1659 | # Return current value of the thread-specific data slot identified by KEY.
1660 | cdef public void *pthread_getspecific(pthread_key_t key):
1661 | if key == __key_limbo:
1662 | return __key_limbo_specific
1663 | if LOCAL_KEY not in __local.__dict__:
1664 | return NULL
1665 | id_ = key
1666 | try:
1667 | pKey = __local.__dict__[id_]
1668 | except KeyError:
1669 | return NULL
1670 | return pKey.specific
1671 |
1672 | # Store POINTER in the thread-specific data slot identified by KEY.
1673 | cdef public int pthread_setspecific \
1674 | (
1675 | pthread_key_t key,
1676 | const void *pointer
1677 | ):
1678 | global __key_limbo_specific
1679 | if key == __key_limbo:
1680 | __key_limbo_specific = pointer
1681 | return 0
1682 | if LOCAL_KEY not in __local.__dict__:
1683 | return libc.errno.ESRCH
1684 | id_ = key
1685 | try:
1686 | pKey = __local.__dict__[id_]
1687 | except KeyError:
1688 | return libc.errno.ESRCH
1689 | pKey.specific = pointer
1690 | return 0
1691 |
1692 | ###########################################################################
1693 | # Install handlers to be called when a new process is created with FORK. #
1694 | # The PREPARE handler is called in the parent process just before #
1695 | # performing FORK. The PARENT handler is called in the parent process #
1696 | # just after FORK. #
1697 | # The CHILD handler is called in the child process. Each of the three #
1698 | # handlers can be NULL, meaning that no handler needs to be called at #
1699 | # that point. #
1700 | # PTHREAD_ATFORK can be called several times, in which case the PREPARE #
1701 | # handlers are called in LIFO order (last added with PTHREAD_ATFORK, #
1702 | # first called before FORK), and the PARENT and CHILD handlers are called #
1703 | # in FIFO (first added, first called). #
1704 | ###########################################################################
1705 |
1706 | cdef public int pthread_atfork \
1707 | (
1708 | void (*prepare) (),
1709 | void (*parent) (),
1710 | void (*child) ()
1711 | ):
1712 | if prepare != NULL:
1713 | try:
1714 | capsule = cpython.pycapsule.PyCapsule_New(
1715 | prepare,
1716 | NULL,
1717 | NULL
1718 | )
1719 | except MemoryError:
1720 | return libc.errno.ENOMEM
1721 | atfork_prepare.append(capsule)
1722 | if parent != NULL:
1723 | try:
1724 | capsule = cpython.pycapsule.PyCapsule_New(
1725 | parent,
1726 | NULL,
1727 | NULL
1728 | )
1729 | except MemoryError:
1730 | return libc.errno.ENOMEM
1731 | atfork_parent.append(capsule)
1732 | if child != NULL:
1733 | try:
1734 | capsule = cpython.pycapsule.PyCapsule_New(
1735 | child,
1736 | NULL,
1737 | NULL
1738 | )
1739 | except MemoryError:
1740 | return libc.errno.ENOMEM
1741 | atfork_child.append(capsule)
1742 | return 0
1743 |
1744 | cdef public posix.types.pid_t fork():
1745 | cdef (void (*) ()) cb
1746 | for capsule in reversed(atfork_prepare):
1747 | cb = \
1748 | cpython.pycapsule.PyCapsule_GetPointer(capsule, NULL)
1749 | cb()
1750 | pid = gevent.os.fork()
1751 | if 0 == pid:
1752 | for capsule in atfork_child:
1753 | cb = \
1754 | cpython.pycapsule.PyCapsule_GetPointer(capsule, NULL)
1755 | cb()
1756 | return 0
1757 | else:
1758 | for capsule in atfork_parent:
1759 | cb = \
1760 | cpython.pycapsule.PyCapsule_GetPointer(capsule, NULL)
1761 | cb()
1762 | return pid
1763 |
--------------------------------------------------------------------------------
/src/c10k/C10kPthread.setup:
--------------------------------------------------------------------------------
1 | import distutils.command.build_ext
2 | import distutils.core
3 | import distutils.extension
4 | import os.path
5 | import sys
6 | import sysconfig
7 |
8 | name = os.path.splitext(os.path.basename(__file__))[0]
9 | home = os.path.abspath(sys.path[0])
10 | extra_compile_args = sysconfig.get_config_var('CFLAGS').split()
11 | extra_compile_args += ['-UCYTHON_REFNANNY']
12 |
13 | distutils.core.setup(
14 | ext_modules=[
15 | distutils.extension.Extension(
16 | name=name,
17 | sources=[os.path.join(home, name + '.c')],
18 | extra_compile_args=extra_compile_args
19 | )
20 | ]
21 | )
22 |
--------------------------------------------------------------------------------
/src/c10k/C10kSocket.pyx:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | # cython: language_level=3
3 |
4 | cimport libc.stdio
5 | cimport libc.stdlib
6 | cimport posix.dlfcn
7 |
8 | cdef extern from '