rcx 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/include/rcx/internal/rcx.hpp +77 -1
- data/include/rcx/internal/rcx_impl.hpp +106 -0
- data/lib/rcx/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 67bf53ccbe670ee75a0faa064fed2c9cedb0f91042c52a61f75d2fa2948f36b7
|
4
|
+
data.tar.gz: cc83c45e03bd7cb72bf8856385c74c061e3995c50f409ff9fbc9beb9a62c12c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '08a0a98e5eade9330c4cd599b25264a3ea792ff5fc7067a122176bbf0a95e766cb6b7d0e6bc583e1bdb8535d49f5ac36624453bfa6455b381564b413e73ee5bb'
|
7
|
+
data.tar.gz: 19d7231302d29d691af29fd21696e43ed9f31be3b293f48c87fc7cd6b73e58e23307a558693cdb60da49d7d8a2a6c553c35bdbb6def1e0efd4ccf847cc07e2da
|
data/CHANGELOG.md
CHANGED
@@ -14,11 +14,11 @@
|
|
14
14
|
#include <string>
|
15
15
|
#include <string_view>
|
16
16
|
#include <type_traits>
|
17
|
-
#include <vector>
|
18
17
|
|
19
18
|
#include <ruby.h>
|
20
19
|
#include <ruby/encoding.h>
|
21
20
|
#include <ruby/io/buffer.h>
|
21
|
+
#include <ruby/thread.h>
|
22
22
|
|
23
23
|
#define rcx_assert(expr) assert((expr))
|
24
24
|
#define rcx_delete(reason) delete
|
@@ -1520,6 +1520,82 @@ namespace rcx {
|
|
1520
1520
|
/// @return Reference to the Ruby environment instance.
|
1521
1521
|
static Ruby &get();
|
1522
1522
|
};
|
1523
|
+
|
1524
|
+
/// Global VM Lock (GVL) management.
|
1525
|
+
///
|
1526
|
+
namespace gvl {
|
1527
|
+
/// Flags for controlling GVL release behavior.
|
1528
|
+
///
|
1529
|
+
enum class ReleaseFlags : int {
|
1530
|
+
/// Default behavior - allow interrupts and no special handling.
|
1531
|
+
None = 0,
|
1532
|
+
/// Prevent interrupt checking during execution.
|
1533
|
+
/// Use this when interrupts could cause problems with your function's execution.
|
1534
|
+
IntrFail = 1,
|
1535
|
+
/// The unblock function (if provided) is async-signal-safe.
|
1536
|
+
UbfAsyncSafe = 2,
|
1537
|
+
/// The function is safe to offload to a background thread or work pool.
|
1538
|
+
Offloadable = 4
|
1539
|
+
};
|
1540
|
+
|
1541
|
+
/// Bitwise OR operator for ReleaseFlags.
|
1542
|
+
constexpr ReleaseFlags operator|(ReleaseFlags lhs, ReleaseFlags rhs) noexcept {
|
1543
|
+
return static_cast<ReleaseFlags>(static_cast<int>(lhs) | static_cast<int>(rhs));
|
1544
|
+
}
|
1545
|
+
|
1546
|
+
/// Bitwise AND operator for ReleaseFlags.
|
1547
|
+
constexpr ReleaseFlags operator&(ReleaseFlags lhs, ReleaseFlags rhs) noexcept {
|
1548
|
+
return static_cast<ReleaseFlags>(static_cast<int>(lhs) & static_cast<int>(rhs));
|
1549
|
+
}
|
1550
|
+
|
1551
|
+
/// Releases the GVL and executes a function.
|
1552
|
+
///
|
1553
|
+
/// This function releases the Global VM Lock (GVL) before executing the
|
1554
|
+
/// callback, allowing other Ruby threads to run concurrently.
|
1555
|
+
///
|
1556
|
+
/// @warning The callback must not call any Ruby C API functions that may touch Ruby
|
1557
|
+
/// objects.
|
1558
|
+
///
|
1559
|
+
/// @tparam F The type of the callback function.
|
1560
|
+
/// @tparam U The type of the unblock function.
|
1561
|
+
/// @param callback The function to execute without the GVL.
|
1562
|
+
/// @param ubf An optional unblock function to interrupt the callback
|
1563
|
+
/// execution. This function can be called from another thread.
|
1564
|
+
/// @param flags Control flags for the GVL release behavior.
|
1565
|
+
/// @return When the callback returns `void`, this function returns `true` if the callback
|
1566
|
+
/// was executed completely, or `false` if it was interrupted by `ubf`.
|
1567
|
+
/// When the callback returns a value, this function returns an `std::optional` containing
|
1568
|
+
/// the result if the callback was executed completely, or `std::nullopt`
|
1569
|
+
/// if it was interrupted.
|
1570
|
+
template <std::invocable<> F, std::invocable U>
|
1571
|
+
auto without_gvl(F callback, std::optional<U> ubf, ReleaseFlags flags) noexcept(noexcept(
|
1572
|
+
callback(), (*ubf)())) -> std::conditional_t<std::is_void_v<std::invoke_result_t<F>>, bool,
|
1573
|
+
std::optional<std::invoke_result_t<F>>>;
|
1574
|
+
|
1575
|
+
/// Releases the GVL and executes a function.
|
1576
|
+
///
|
1577
|
+
/// This is an overload of `without_gvl` that does not take an unblock
|
1578
|
+
/// function.
|
1579
|
+
///
|
1580
|
+
/// @warning The callback must not call any Ruby C API functions that may touch Ruby
|
1581
|
+
/// objects.
|
1582
|
+
///
|
1583
|
+
/// @tparam F The type of the callback function.
|
1584
|
+
/// @param callback The function to execute without the GVL.
|
1585
|
+
/// @param flags Control flags for the GVL release behavior.
|
1586
|
+
/// @return When the callback returns `void`, this function returns `true` if the callback
|
1587
|
+
/// was executed completely, or `false` if it was interrupted.
|
1588
|
+
/// When the callback returns a value, this function returns an `std::optional` containing
|
1589
|
+
/// the result if the callback was executed completely, or `std::nullopt`
|
1590
|
+
/// if it was interrupted.
|
1591
|
+
template <std::invocable<> F>
|
1592
|
+
auto without_gvl(F &&callback, ReleaseFlags flags) noexcept(noexcept(callback()))
|
1593
|
+
-> std::conditional_t<std::is_void_v<std::invoke_result_t<F>>, bool,
|
1594
|
+
std::optional<std::invoke_result_t<F>>>;
|
1595
|
+
|
1596
|
+
/// Checks for pending interrupts.
|
1597
|
+
void check_interrupts();
|
1598
|
+
}
|
1523
1599
|
}
|
1524
1600
|
|
1525
1601
|
namespace std {
|
@@ -3,6 +3,7 @@
|
|
3
3
|
|
4
4
|
#include <concepts>
|
5
5
|
#include <memory>
|
6
|
+
#include <optional>
|
6
7
|
#include <ranges>
|
7
8
|
#include <stdexcept>
|
8
9
|
#include <string_view>
|
@@ -10,6 +11,7 @@
|
|
10
11
|
#include <type_traits>
|
11
12
|
#include <typeinfo>
|
12
13
|
#include <utility>
|
14
|
+
#include <variant>
|
13
15
|
|
14
16
|
#include <ffi.h>
|
15
17
|
#include <rcx/internal/rcx.hpp>
|
@@ -1414,4 +1416,108 @@ namespace rcx {
|
|
1414
1416
|
}
|
1415
1417
|
}
|
1416
1418
|
}
|
1419
|
+
|
1420
|
+
namespace gvl {
|
1421
|
+
template <std::invocable<> F, std::invocable<> U>
|
1422
|
+
auto without_gvl(F callback, std::optional<U> ubf, ReleaseFlags flags) noexcept(noexcept(
|
1423
|
+
callback(), (*ubf)())) -> std::conditional_t<std::is_void_v<std::invoke_result_t<F>>, bool,
|
1424
|
+
std::optional<std::invoke_result_t<F>>> {
|
1425
|
+
|
1426
|
+
using ResultType = std::conditional_t<std::is_void_v<std::invoke_result_t<F>>, std::monostate,
|
1427
|
+
std::optional<std::invoke_result_t<F>>>;
|
1428
|
+
|
1429
|
+
struct CallbackData {
|
1430
|
+
F callback;
|
1431
|
+
[[no_unique_address]] ResultType result;
|
1432
|
+
std::exception_ptr exception;
|
1433
|
+
};
|
1434
|
+
|
1435
|
+
struct UbfData {
|
1436
|
+
U ubf;
|
1437
|
+
std::exception_ptr exception;
|
1438
|
+
};
|
1439
|
+
|
1440
|
+
CallbackData data{std::move(callback), ResultType{}, nullptr};
|
1441
|
+
std::optional<UbfData> ubf_data;
|
1442
|
+
if(ubf) {
|
1443
|
+
ubf_data.emplace(std::move(*ubf), nullptr);
|
1444
|
+
}
|
1445
|
+
|
1446
|
+
auto callback_wrapper = [](void *RCX_Nonnull arg) -> void * {
|
1447
|
+
auto &data = *static_cast<CallbackData * RCX_Nonnull>(arg);
|
1448
|
+
try {
|
1449
|
+
if constexpr(std::is_void_v<std::invoke_result_t<F>>) {
|
1450
|
+
data.callback();
|
1451
|
+
} else {
|
1452
|
+
data.result = data.callback();
|
1453
|
+
}
|
1454
|
+
} catch(...) {
|
1455
|
+
data.exception = std::current_exception();
|
1456
|
+
}
|
1457
|
+
return reinterpret_cast<void *>(1); // Non-null to indicate execution
|
1458
|
+
};
|
1459
|
+
|
1460
|
+
using Ubf = void (*RCX_Nullable)(void *RCX_Nonnull);
|
1461
|
+
Ubf ubf_wrapper = nullptr;
|
1462
|
+
if(ubf_data) {
|
1463
|
+
ubf_wrapper = [](void *RCX_Nonnull arg) -> void {
|
1464
|
+
auto &data = *static_cast<UbfData * RCX_Nonnull>(arg);
|
1465
|
+
try {
|
1466
|
+
data.ubf();
|
1467
|
+
} catch(...) {
|
1468
|
+
data.exception = std::current_exception();
|
1469
|
+
}
|
1470
|
+
};
|
1471
|
+
}
|
1472
|
+
|
1473
|
+
void *result = rb_nogvl(callback_wrapper, &data, ubf_wrapper,
|
1474
|
+
ubf_data ? std::addressof(*ubf_data) : nullptr, static_cast<int>(flags));
|
1475
|
+
|
1476
|
+
// Check for UBF exceptions first. The callback was cancelled with UBF, which then raised.
|
1477
|
+
if(ubf_data && ubf_data->exception) {
|
1478
|
+
std::rethrow_exception(ubf_data->exception);
|
1479
|
+
}
|
1480
|
+
|
1481
|
+
// If rb_nogvl returned nullptr, the callback was cancelled.
|
1482
|
+
if(result == nullptr) {
|
1483
|
+
if constexpr(std::is_void_v<std::invoke_result_t<F>>) {
|
1484
|
+
return false;
|
1485
|
+
} else {
|
1486
|
+
return std::nullopt;
|
1487
|
+
}
|
1488
|
+
}
|
1489
|
+
|
1490
|
+
// Re-throw any exception that occurred in the callback.
|
1491
|
+
if(data.exception) {
|
1492
|
+
std::rethrow_exception(data.exception);
|
1493
|
+
}
|
1494
|
+
|
1495
|
+
// Return the callback result.
|
1496
|
+
if constexpr(std::is_void_v<std::invoke_result_t<F>>) {
|
1497
|
+
return true;
|
1498
|
+
} else {
|
1499
|
+
return std::move(data.result);
|
1500
|
+
}
|
1501
|
+
}
|
1502
|
+
|
1503
|
+
template <std::invocable<> F, std::invocable<> U>
|
1504
|
+
auto without_gvl(F &&callback, U ubf, ReleaseFlags flags) noexcept(noexcept(callback(), ubf()))
|
1505
|
+
-> std::conditional_t<std::is_void_v<std::invoke_result_t<F>>, bool,
|
1506
|
+
std::optional<std::invoke_result_t<F>>> {
|
1507
|
+
return without_gvl(
|
1508
|
+
std::forward<F>(callback), std::optional<std::remove_cvref_t<U>>(std::move(ubf)), flags);
|
1509
|
+
}
|
1510
|
+
|
1511
|
+
template <std::invocable<> F>
|
1512
|
+
auto without_gvl(F &&callback, ReleaseFlags flags) noexcept(noexcept(callback()))
|
1513
|
+
-> std::conditional_t<std::is_void_v<std::invoke_result_t<F>>, bool,
|
1514
|
+
std::optional<std::invoke_result_t<F>>> {
|
1515
|
+
using DefaultUbf = void (*)();
|
1516
|
+
return without_gvl(std::forward<F>(callback), std::optional<DefaultUbf>(std::nullopt), flags);
|
1517
|
+
}
|
1518
|
+
|
1519
|
+
inline void check_interrupts() {
|
1520
|
+
detail::protect([]() noexcept { ::rb_thread_check_ints(); });
|
1521
|
+
}
|
1522
|
+
}
|
1417
1523
|
}
|
data/lib/rcx/version.rb
CHANGED