| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 3dd11c4 commit 9b04f82
6 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -715,6 +715,25 @@ This is unsafe and dangerous. The returned pointer can become invalid if the | |||
| 715 | 715 | underlying memory is detached, resized, transferred, or otherwise invalidated. | |
| 716 | 716 | Using stale pointers can cause memory corruption or process crashes. | |
| 717 | 717 | ||
| 718 | + ## `ffi.getCurrentEventLoop()` | ||
| 719 | + | ||
| 720 | + <!-- YAML | ||
| 721 | + added: REPLACEME | ||
| 722 | + --> | ||
| 723 | + | ||
| 724 | + * Returns: {bigint} | ||
| 725 | + | ||
| 726 | + Returns the address of the current thread's `uv_loop_t` as a `bigint`. | ||
| 727 | + | ||
| 728 | + The returned address is for the current Node.js environment. In the main thread, | ||
| 729 | + this is the main thread event loop. In a worker thread, this is that worker's | ||
| 730 | + event loop. | ||
| 731 | + | ||
| 732 | + This is unsafe and dangerous. The returned pointer is only valid for the lifetime | ||
| 733 | + of the current environment. Using it after the environment exits, or from native | ||
| 734 | + code that assumes a different thread or lifetime, can crash the process or | ||
| 735 | + corrupt memory. | ||
| 736 | + | ||
| 718 | 737 | ## Safety notes | |
| 719 | 738 | ||
| 720 | 739 | The `node:ffi` module does not track pointer validity, memory ownership, or | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -42,6 +42,7 @@ const { | |||
| 42 | 42 | getUint64, | |
| 43 | 43 | getFloat32, | |
| 44 | 44 | getFloat64, | |
| 45 | + getCurrentEventLoop, | ||
| 45 | 46 | exportBytes, | |
| 46 | 47 | getRawPointer, | |
| 47 | 48 | kFastArguments, | |
@@ -320,6 +321,7 @@ module.exports = { | |||
| 320 | 321 | getUint64, | |
| 321 | 322 | getFloat32, | |
| 322 | 323 | getFloat64, | |
| 324 | + getCurrentEventLoop, | ||
| 323 | 325 | getRawPointer, | |
| 324 | 326 | setInt8, | |
| 325 | 327 | setUint8, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1215,6 +1215,17 @@ Local<FunctionTemplate> DynamicLibrary::GetConstructorTemplate( | |||
| 1215 | 1215 | return tmpl; | |
| 1216 | 1216 | } | |
| 1217 | 1217 | ||
| 1218 | + void GetCurrentEventLoop(const FunctionCallbackInfo<Value>& args) { | ||
| 1219 | + Environment* env = Environment::GetCurrent(args); | ||
| 1220 | + Isolate* isolate = env->isolate(); | ||
| 1221 | + | ||
| 1222 | + THROW_IF_INSUFFICIENT_PERMISSIONS(env, permission::PermissionScope::kFFI, ""); | ||
| 1223 | + | ||
| 1224 | + args.GetReturnValue().Set(BigInt::NewFromUnsigned( | ||
| 1225 | + isolate, | ||
| 1226 | + static_cast<uint64_t>(reinterpret_cast<uintptr_t>(env->event_loop())))); | ||
| 1227 | + } | ||
| 1228 | + | ||
| 1218 | 1229 | // Module initialization. | |
| 1219 | 1230 | static void Initialize(Local<Object> target, | |
| 1220 | 1231 | Local<Value> unused, | |
@@ -1230,6 +1241,7 @@ static void Initialize(Local<Object> target, | |||
| 1230 | 1241 | SetMethod(context, target, "toArrayBuffer", ToArrayBuffer); | |
| 1231 | 1242 | SetMethod(context, target, "exportBytes", ExportBytes); | |
| 1232 | 1243 | SetMethod(context, target, "getRawPointer", GetRawPointer); | |
| 1244 | + SetMethod(context, target, "getCurrentEventLoop", GetCurrentEventLoop); | ||
| 1233 | 1245 | ||
| 1234 | 1246 | SetMethod(context, target, "getInt8", GetInt8); | |
| 1235 | 1247 | SetMethod(context, target, "getUint8", GetUint8); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -178,6 +178,7 @@ void ToBuffer(const v8::FunctionCallbackInfo<v8::Value>& args); | |||
| 178 | 178 | void ToArrayBuffer(const v8::FunctionCallbackInfo<v8::Value>& args); | |
| 179 | 179 | void ExportBytes(const v8::FunctionCallbackInfo<v8::Value>& args); | |
| 180 | 180 | void GetRawPointer(const v8::FunctionCallbackInfo<v8::Value>& args); | |
| 181 | + void GetCurrentEventLoop(const v8::FunctionCallbackInfo<v8::Value>& args); | ||
| 181 | 182 | ||
| 182 | 183 | } // namespace node::ffi | |
| 183 | 184 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,6 +5,7 @@ const { spawnSyncAndAssert } = require('../common/child_process'); | |||
| 5 | 5 | const assert = require('node:assert'); | |
| 6 | 6 | const { spawnSync } = require('node:child_process'); | |
| 7 | 7 | const { test } = require('node:test'); | |
| 8 | + const { Worker } = require('node:worker_threads'); | ||
| 8 | 9 | ||
| 9 | 10 | common.skipIfFFIMissing(); | |
| 10 | 11 | ||
@@ -87,6 +88,7 @@ test('ffi exports expected API surface', () => { | |||
| 87 | 88 | 'exportArrayBufferView', | |
| 88 | 89 | 'exportBuffer', | |
| 89 | 90 | 'exportString', | |
| 91 | + 'getCurrentEventLoop', | ||
| 90 | 92 | 'getFloat32', | |
| 91 | 93 | 'getFloat64', | |
| 92 | 94 | 'getInt16', | |
@@ -135,6 +137,7 @@ test('ffi exports expected API surface', () => { | |||
| 135 | 137 | assert.strictEqual(typeof ffi.getUint64, 'function'); | |
| 136 | 138 | assert.strictEqual(typeof ffi.getFloat32, 'function'); | |
| 137 | 139 | assert.strictEqual(typeof ffi.getFloat64, 'function'); | |
| 140 | + assert.strictEqual(typeof ffi.getCurrentEventLoop, 'function'); | ||
| 138 | 141 | assert.strictEqual(typeof ffi.setInt8, 'function'); | |
| 139 | 142 | assert.strictEqual(typeof ffi.setUint8, 'function'); | |
| 140 | 143 | assert.strictEqual(typeof ffi.setInt16, 'function'); | |
@@ -180,3 +183,33 @@ test('ffi.types exports canonical type constants', () => { | |||
| 180 | 183 | assert.deepStrictEqual(ffi.types, expected); | |
| 181 | 184 | assert.strictEqual(Object.isFrozen(ffi.types), true); | |
| 182 | 185 | }); | |
| 186 | + | ||
| 187 | + test('ffi.getCurrentEventLoop returns the current thread event loop address', async () => { | ||
| 188 | + const ffi = require('node:ffi'); | ||
| 189 | + const mainLoop = ffi.getCurrentEventLoop(); | ||
| 190 | + | ||
| 191 | + assert.strictEqual(typeof mainLoop, 'bigint'); | ||
| 192 | + assert.ok(mainLoop > 0n); | ||
| 193 | + assert.strictEqual(ffi.getCurrentEventLoop(), mainLoop); | ||
| 194 | + | ||
| 195 | + const workerLoop = await new Promise((resolve, reject) => { | ||
| 196 | + const worker = new Worker(` | ||
| 197 | + const { parentPort } = require('node:worker_threads'); | ||
| 198 | + const ffi = require('node:ffi'); | ||
| 199 | + const loop = ffi.getCurrentEventLoop(); | ||
| 200 | + | ||
| 201 | + parentPort.postMessage([loop, ffi.getCurrentEventLoop()]); | ||
| 202 | + `, { eval: true }); | ||
| 203 | + | ||
| 204 | + worker.on('message', resolve); | ||
| 205 | + worker.on('error', reject); | ||
| 206 | + worker.on('exit', (code) => { | ||
| 207 | + if (code !== 0) reject(new Error(`Worker stopped with exit code ${code}`)); | ||
| 208 | + }); | ||
| 209 | + }); | ||
| 210 | + | ||
| 211 | + assert.strictEqual(typeof workerLoop[0], 'bigint'); | ||
| 212 | + assert.ok(workerLoop[0] > 0n); | ||
| 213 | + assert.strictEqual(workerLoop[0], workerLoop[1]); | ||
| 214 | + assert.notStrictEqual(workerLoop[0], mainLoop); | ||
| 215 | + }); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -70,6 +70,10 @@ test('permission model blocks ffi memory and helper APIs', () => { | |||
| 70 | 70 | ffi.getRawPointer(Buffer.alloc(0)); | |
| 71 | 71 | }, denied); | |
| 72 | 72 | ||
| 73 | + assert.throws(() => { | ||
| 74 | + ffi.getCurrentEventLoop(); | ||
| 75 | + }, denied); | ||
| 76 | + | ||
| 73 | 77 | assert.throws(() => { | |
| 74 | 78 | ffi.dlclose({ close() {} }); | |
| 75 | 79 | }, denied); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments