]>
Commit | Line | Data |
---|---|---|
31f18b77 FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2017 SUSE LLC | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | ||
15 | #ifndef GIL_H_ | |
16 | #define GIL_H_ | |
17 | ||
18 | #include "Python.h" | |
19 | ||
20 | #include "common/debug.h" | |
21 | ||
22 | #define dout_context g_ceph_context | |
23 | #define dout_subsys ceph_subsys_mgr | |
24 | #undef dout_prefix | |
25 | #define dout_prefix *_dout << "mgr " << __func__ << " " | |
26 | ||
27 | // | |
28 | // Use one of these in any scope in which you need to hold Python's | |
29 | // Global Interpreter Lock. | |
30 | // | |
31 | // Do *not* nest these, as a second GIL acquire will deadlock (see | |
32 | // https://docs.python.org/2/c-api/init.html#c.PyEval_RestoreThread) | |
33 | // | |
34 | // If in doubt, explicitly put a scope around the block of code you | |
35 | // know you need the GIL in. | |
36 | // | |
37 | // See the comment below for when to set new_thread == true | |
38 | // | |
39 | class Gil { | |
40 | public: | |
41 | Gil(const Gil&) = delete; | |
42 | Gil& operator=(const Gil&) = delete; | |
43 | ||
44 | Gil(PyThreadState *ts, bool new_thread = false) : pThreadState(ts) | |
45 | { | |
46 | assert(pThreadState != nullptr); | |
47 | ||
48 | // Acquire the GIL, set the current thread state | |
49 | PyEval_RestoreThread(pThreadState); | |
50 | dout(20) << "GIL acquired for thread state " << pThreadState << dendl; | |
51 | ||
52 | // | |
53 | // If called from a separate OS thread (i.e. a thread not created | |
54 | // by Python, that does't already have a python thread state that | |
55 | // was created when that thread was active), we need to manually | |
56 | // create and switch to a python thread state specifically for this | |
57 | // OS thread. | |
58 | // | |
59 | // Note that instead of requring the caller to set new_thread == true | |
60 | // when calling this from a separate OS thread, we could figure out | |
61 | // if this was necessary automatically, as follows: | |
62 | // | |
63 | // if (pThreadState->thread_id != PyThread_get_thread_ident()) { | |
64 | // | |
65 | // However, this means we're accessing pThreadState->thread_id, but | |
66 | // the Python C API docs say that "The only public data member is | |
67 | // PyInterpreterState *interp", i.e. doing this would violate | |
68 | // something that's meant to be a black box. | |
69 | // | |
70 | if (new_thread) { | |
71 | pNewThreadState = PyThreadState_New(pThreadState->interp); | |
72 | PyThreadState_Swap(pNewThreadState); | |
73 | dout(20) << "Switched to new thread state " << pNewThreadState << dendl; | |
74 | } | |
75 | } | |
76 | ||
77 | ~Gil() | |
78 | { | |
79 | if (pNewThreadState != nullptr) { | |
80 | dout(20) << "Destroying new thread state " << pNewThreadState << dendl; | |
81 | PyThreadState_Swap(pThreadState); | |
82 | PyThreadState_Clear(pNewThreadState); | |
83 | PyThreadState_Delete(pNewThreadState); | |
84 | } | |
85 | // Release the GIL, reset the thread state to NULL | |
86 | PyEval_SaveThread(); | |
87 | dout(20) << "GIL released for thread state " << pThreadState << dendl; | |
88 | } | |
89 | ||
90 | private: | |
91 | PyThreadState *pThreadState; | |
92 | PyThreadState *pNewThreadState = nullptr; | |
93 | }; | |
94 | ||
95 | #endif | |
96 |