Skip to content

Commit 8aab622

Browse files
Merge branch 'caleb/fix/this' into philippe/pyTypeFactory-cleanup
2 parents 3cb7ebc + c54d5e8 commit 8aab622

File tree

8 files changed

+282
-46
lines changed

8 files changed

+282
-46
lines changed

.github/workflows/test-and-publish.yaml

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@ on:
88
- '*'
99
workflow_call:
1010
workflow_dispatch:
11+
inputs:
12+
debug_enabled:
13+
type: boolean
14+
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
15+
required: false
16+
default: false
17+
dump_cores:
18+
type: boolean
19+
description: 'Include core dumps in CI artifacts'
20+
required: false
21+
default: false
1122
pull_request:
1223

1324
env:
@@ -31,13 +42,13 @@ jobs:
3142
python_version: [ '3.10' ]
3243
runs-on: ${{ matrix.os }}
3344
steps:
34-
- uses: actions/checkout@v3
35-
- uses: actions/setup-python@v4
45+
- uses: actions/checkout@v4
46+
- uses: actions/setup-python@v5
3647
with:
3748
python-version: ${{ matrix.python_version }}
3849
- name: Cache spidermonkey build
3950
id: cache-spidermonkey
40-
uses: actions/cache@v3
51+
uses: actions/cache@v4
4152
with:
4253
path: |
4354
./_spidermonkey_install/*
@@ -54,10 +65,10 @@ jobs:
5465
build-spidermonkey-win:
5566
runs-on: windows-2019
5667
steps:
57-
- uses: actions/checkout@v3
68+
- uses: actions/checkout@v4
5869
- name: Cache spidermonkey build
5970
id: cache-spidermonkey
60-
uses: actions/cache@v3
71+
uses: actions/cache@v4
6172
with:
6273
path: |
6374
./_spidermonkey_install/*
@@ -104,12 +115,12 @@ jobs:
104115
python_version: '3.9'
105116
runs-on: ${{ matrix.os }}
106117
steps:
107-
- uses: actions/checkout@v3
118+
- uses: actions/checkout@v4
108119
with:
109120
submodules: recursive
110121
fetch-depth: 0 # fetch all history for all branches and tags
111122
# poetry-dynamic-versioning needs git tags to produce the correct version number
112-
- uses: actions/setup-python@v4
123+
- uses: actions/setup-python@v5
113124
with:
114125
python-version: ${{ matrix.python_version }}
115126
- name: Setup Poetry
@@ -138,7 +149,7 @@ jobs:
138149
poetry install --no-root --only=dev
139150
echo "Installed Dependencies"
140151
- name: Use cached spidermonkey build
141-
uses: actions/cache@v3
152+
uses: actions/cache@v4
142153
with:
143154
path: |
144155
./_spidermonkey_install/*
@@ -168,13 +179,16 @@ jobs:
168179
name: docs-${{ github.run_id }}-${{ github.sha }}
169180
path: ./build/docs/html/
170181
- name: Set cores to get stored in /cores
182+
if: ${{ matrix.os != 'windows-2019' && github.event_name == 'workflow_dispatch' && inputs.dump_cores }}
183+
# TODO (Caleb Aikens) figure out how to get Windows core dumps
171184
run: |
172-
if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]]; then
173-
# TODO (Caleb Aikens) figure out how to get Windows core dumps
174-
sudo mkdir /cores
175-
sudo chmod 777 /cores
176-
# Core filenames will be of the form executable.pid.timestamp:
177-
sudo bash -c 'echo "/cores/%e.%p.%t" > /proc/sys/kernel/core_pattern'
185+
sudo mkdir -p /cores
186+
sudo chmod 777 /cores
187+
# Core filenames will be of the form osname.pythonversion.executable.pid.timestamp:
188+
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
189+
sudo bash -c 'echo "/cores/${OSTYPE}.$(poetry run python --version).%e.%p.%t" > /proc/sys/kernel/core_pattern'
190+
else
191+
sudo sysctl kern.corefile="/cores/${OSTYPE}.$(poetry run python --version).%e.%p.%y"
178192
fi
179193
- name: Run Python tests (pytest)
180194
run: |
@@ -185,25 +199,34 @@ jobs:
185199
poetry run python -m pip install --force-reinstall --verbose ./dist/*
186200
poetry run python -m pytest tests/python
187201
- name: Run JS tests (peter-jr)
202+
if: ${{ (success() || failure()) }}
188203
run: |
189204
if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]]; then
190205
# TODO (Caleb Aikens) figure out how to get Windows core dumps
191206
ulimit -c unlimited
192207
fi
193208
poetry run bash ./peter-jr ./tests/js/
194-
- uses: actions/upload-artifact@v3
195-
if: ${{ failure() && matrix.os != 'windows-2019' }} # Run only if something went wrong
209+
- name: Upload core dumps as CI artifacts
210+
uses: actions/upload-artifact@v3
211+
if: ${{ matrix.os != 'windows-2019' && github.event_name == 'workflow_dispatch' && inputs.dump_cores }}
196212
# TODO (Caleb Aikens) figure out how to get Windows core dumps
197213
with:
198-
name: cores
214+
name: cores-${{ matrix.os }}-${{ matrix.python_version }}
199215
path: /cores
216+
# Enable tmate debugging of manually-triggered workflows if the input option was provided
217+
- name: SSH debug session
218+
if: ${{ (success() || failure()) && github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
219+
uses: mxschmitt/action-tmate@v3
220+
with:
221+
detached: true
222+
limit-access-to-actor: true
200223
sdist:
201224
runs-on: ubuntu-20.04
202225
steps:
203-
- uses: actions/checkout@v3
226+
- uses: actions/checkout@v4
204227
with:
205228
fetch-depth: 0
206-
- uses: actions/setup-python@v4
229+
- uses: actions/setup-python@v5
207230
with:
208231
python-version: '3.9'
209232
- name: Setup Poetry
@@ -226,7 +249,7 @@ jobs:
226249
if: ${{ success() && github.event_name == 'push' && contains(github.ref, 'refs/tags/') }}
227250
steps:
228251
# no need to checkout
229-
- uses: actions/setup-python@v4
252+
- uses: actions/setup-python@v5
230253
with:
231254
python-version: '3.9'
232255
- run: pip install twine

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
6161
set(Python_FIND_VIRTUALENV FIRST) # (require cmake >= v3.15 and this is the default) use the Python version configured by pyenv if available
6262
set(PYTHON_LIBRARIES ${Python_LIBRARIES})
6363
set(PYTHON_INCLUDE_DIR ${Python_INCLUDE_DIRS})
64+
message("Linux - Using Python:${Python_VERSION_MAJOR}.${Python_VERSION_MINOR} - Libraries:${PYTHON_LIBRARIES} - IncludeDirs: ${PYTHON_INCLUDE_DIR}")
6465
find_package(SpiderMonkey REQUIRED)
6566
elseif(WIN32)
6667
find_package(PythonInterp 3.8 REQUIRED)

include/PyEventLoop.hh

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,21 @@ public:
3131
* @see https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.Handle
3232
*/
3333
struct AsyncHandle {
34+
using id_t = uint32_t;
35+
using id_ptr_pair = std::pair<id_t, AsyncHandle *>;
3436
public:
3537
explicit AsyncHandle(PyObject *handle) : _handle(handle) {};
3638
AsyncHandle(const AsyncHandle &old) = delete; // forbid copy-initialization
37-
AsyncHandle(AsyncHandle &&old) : _handle(std::exchange(old._handle, nullptr)) {}; // clear the moved-from object
39+
AsyncHandle(AsyncHandle &&old) : _handle(std::exchange(old._handle, nullptr)), _refed(old._refed.exchange(false)) {}; // clear the moved-from object
3840
~AsyncHandle() {
3941
if (Py_IsInitialized()) { // the Python runtime has already been finalized when `_timeoutIdMap` is cleared at exit
4042
Py_XDECREF(_handle);
4143
}
4244
}
45+
static inline id_ptr_pair newEmpty() {
46+
auto handle = AsyncHandle(Py_None);
47+
return AsyncHandle::getUniqueIdAndPtr(std::move(handle));
48+
}
4349

4450
/**
4551
* @brief Cancel the scheduled event-loop job.
@@ -51,18 +57,23 @@ public:
5157
* @brief Get the unique `timeoutID` for JS `setTimeout`/`clearTimeout` methods
5258
* @see https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#return_value
5359
*/
54-
static inline uint32_t getUniqueId(AsyncHandle &&handle) {
60+
static inline id_t getUniqueId(AsyncHandle &&handle) {
5561
// TODO (Tom Tang): mutex lock
5662
_timeoutIdMap.push_back(std::move(handle));
5763
return _timeoutIdMap.size() - 1; // the index in `_timeoutIdMap`
5864
}
59-
static inline AsyncHandle *fromId(uint32_t timeoutID) {
65+
static inline AsyncHandle *fromId(id_t timeoutID) {
6066
try {
6167
return &_timeoutIdMap.at(timeoutID);
6268
} catch (...) { // std::out_of_range&
6369
return nullptr; // invalid timeoutID
6470
}
6571
}
72+
static inline id_ptr_pair getUniqueIdAndPtr(AsyncHandle &&handle) {
73+
auto timeoutID = getUniqueId(std::move(handle));
74+
auto ptr = fromId(timeoutID);
75+
return std::make_pair(timeoutID, ptr);
76+
}
6677

6778
/**
6879
* @brief Get the underlying `asyncio.Handle` Python object
@@ -71,8 +82,44 @@ public:
7182
Py_INCREF(_handle); // otherwise the object would be GC-ed as the AsyncHandle destructor decreases the reference count
7283
return _handle;
7384
}
85+
86+
/**
87+
* @brief Replace the underlying `asyncio.Handle` Python object with the provided value
88+
* @return the old `asyncio.Handle` object
89+
*/
90+
inline PyObject *swap(PyObject *newHandleObject) {
91+
return std::exchange(_handle, newHandleObject);
92+
}
93+
94+
/**
95+
* @brief Getter for if the timer has been ref'ed
96+
*/
97+
inline bool hasRef() {
98+
return _refed;
99+
}
100+
101+
/**
102+
* @brief Ref the timer so that the event-loop won't exit as long as the timer is active
103+
*/
104+
inline void addRef() {
105+
if (!_refed) {
106+
_refed = true;
107+
PyEventLoop::_locker->incCounter();
108+
}
109+
}
110+
111+
/**
112+
* @brief Unref the timer so that the event-loop can exit
113+
*/
114+
inline void removeRef() {
115+
if (_refed) {
116+
_refed = false;
117+
PyEventLoop::_locker->decCounter();
118+
}
119+
}
74120
protected:
75121
PyObject *_handle;
122+
std::atomic_bool _refed = false;
76123
};
77124

78125
/**
@@ -85,9 +132,9 @@ public:
85132
* @brief Schedule a job to the Python event-loop, with the given delay
86133
* @param jobFn - The JS event-loop job converted to a Python function
87134
* @param delaySeconds - The job function will be called after the given number of seconds
88-
* @return a AsyncHandle, the value can be safely ignored
135+
* @return the timeoutId and a pointer to the AsyncHandle
89136
*/
90-
AsyncHandle enqueueWithDelay(PyObject *jobFn, double delaySeconds);
137+
[[nodiscard]] AsyncHandle::id_ptr_pair enqueueWithDelay(PyObject *jobFn, double delaySeconds);
91138

92139
/**
93140
* @brief C++ wrapper for Python `asyncio.Future` class

python/pythonmonkey/builtin_modules/internal-binding.d.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,21 @@ declare function internalBinding(namespace: "timers"): {
4545
* internal binding helper for the `clearTimeout` global function
4646
*/
4747
cancelByTimeoutId(timeoutId: number): void;
48+
49+
/**
50+
* internal binding helper for if a timer object has been ref'ed
51+
*/
52+
timerHasRef(timeoutId: number): boolean;
53+
54+
/**
55+
* internal binding helper for ref'ing the timer
56+
*/
57+
timerAddRef(timeoutId: number): void;
58+
59+
/**
60+
* internal binding helper for unref'ing the timer
61+
*/
62+
timerRemoveRef(timeoutId: number): void;
4863
};
4964

5065
export = internalBinding;

0 commit comments

Comments
 (0)