Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 62 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
- uses: actions/checkout@v4

- name: Install Zig
uses: mlugg/setup-zig@v2
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
with:
version: "0.16.0"

Expand Down Expand Up @@ -92,7 +92,7 @@ jobs:
- uses: actions/checkout@v4

- name: Install Zig
uses: mlugg/setup-zig@v2
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
with:
version: "0.16.0"

Expand Down Expand Up @@ -235,7 +235,7 @@ jobs:
uses: actions/checkout@v4

- name: Install Zig
uses: mlugg/setup-zig@v2
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
with:
version: "0.16.0"

Expand Down Expand Up @@ -294,7 +294,7 @@ jobs:
uses: actions/checkout@v4

- name: Install Zig
uses: mlugg/setup-zig@v2
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
with:
version: "0.16.0"

Expand Down Expand Up @@ -517,6 +517,59 @@ jobs:
--sub-marker "subscriber: done" --pub-marker "publisher: done" --timeout 60 \
--ld-library-path "$GITHUB_WORKSPACE/zig-out/lib"

# ── Stress tests ─────────────────────────────────────────────────────────
#
# stress-tests/ -- concurrency- and lifecycle-under-load tests, OpenDDS
# EntityLifecycleStress-shaped. Deliberately outside `zig build test`
# (wall-clock, real UDP, non-deterministic). Mirrors the `examples` job:
# build zzdds native + -Ddebug-allocator, then run_all.py --strict with
# CI-sized parameters. run_all.py's own xfail list keeps a currently-known
# zzdds bug (see stress-tests/README.md "Findings") from reddening the gate
# while still exercising it. The nightly `schedule` trigger runs a heavier
# matrix below.
stress:
name: stress
needs: test-linux
runs-on: ubuntu-latest
timeout-minutes: 25

steps:
- uses: actions/checkout@v4

- name: Install Zig
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
with:
version: "0.16.0"

- name: Install Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

# setarch -R (disable ASLR) is applied per-process by _common.LiveProcess
# for the TSan lane; this lowers the shadow-map-incompatible entropy for
# anything that slips through (build-time compiler probes etc.).
- name: Reduce ASLR entropy (for the TSan churn lane)
run: sudo sysctl -w vm.mmap_rnd_bits=28

- name: Build zzdds (native, DebugAllocator)
run: zig build -Ddebug-allocator=true install

- name: Run stress suite (strict)
run: stress-tests/run_all.py --strict

- name: lifecycle_churn/reentrant under ThreadSanitizer
working-directory: stress-tests/zig/lifecycle_churn
run: ./run.py --scenario reentrant --threads 4 --iterations 25 --tsan --timeout 300

# Weekly full-vendor sweep trigger only: heavier, longer, + --large.
- name: Nightly heavy matrix
if: github.event_name == 'schedule'
working-directory: stress-tests/zig/entity_lifecycle_stress
run: |
./run.py --publishers 12 --subscribers 12 --samples 750 --churn --timeout 300
./run.py --publishers 6 --subscribers 6 --large --churn --timeout 300

# ── Coverage ──────────────────────────────────────────────────────────────
#
# Builds all test binaries to zig-out/tests/, runs each through kcov
Expand All @@ -542,7 +595,7 @@ jobs:
- uses: actions/checkout@v4

- name: Install Zig
uses: mlugg/setup-zig@v2
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
with:
version: "0.16.0"

Expand Down Expand Up @@ -592,7 +645,7 @@ jobs:
- uses: actions/checkout@v4

- name: Install Zig
uses: mlugg/setup-zig@v2
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
with:
version: "0.16.0"

Expand Down Expand Up @@ -817,7 +870,7 @@ jobs:
(cd dds-rtps/srcZig/zzdds && python3 point_zon_at_symlinks.py)

- name: Install Zig
uses: mlugg/setup-zig@v2
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
with:
version: "0.16.0"

Expand Down Expand Up @@ -890,7 +943,7 @@ jobs:
(cd dds-rtps/srcZig/zzdds && python3 point_zon_at_symlinks.py)

- name: Install Zig
uses: mlugg/setup-zig@v2
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
with:
version: "0.16.0"

Expand Down Expand Up @@ -1160,7 +1213,7 @@ jobs:
(cd dds-rtps/srcZig/zzdds && python3 point_zon_at_symlinks.py)

- name: Install Zig
uses: mlugg/setup-zig@v2
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
with:
version: "0.16.0"

Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
echo "Tag will be: ${TAG}"

- name: Install Zig
uses: mlugg/setup-zig@v2
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
with:
version: ${{ steps.ver.outputs.zig_version }}

Expand Down Expand Up @@ -83,7 +83,7 @@ jobs:
- uses: actions/checkout@v4

- name: Install Zig
uses: mlugg/setup-zig@v2
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
with:
version: ${{ needs.prepare.outputs.zig_version }}

Expand Down Expand Up @@ -211,7 +211,7 @@ jobs:
(cd dds-rtps/srcZig/zzdds && python3 point_zon_at_symlinks.py)

- name: Install Zig
uses: mlugg/setup-zig@v2
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
with:
version: ${{ needs.prepare.outputs.zig_version }}

Expand Down Expand Up @@ -278,7 +278,7 @@ jobs:
- uses: actions/checkout@v4

- name: Install Zig
uses: mlugg/setup-zig@v2
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
with:
version: ${{ needs.prepare.outputs.zig_version }}

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,4 @@ test/interop/interop-artifacts-*.tar.gz
*~
.idea/
.vscode/
__pycache__/
68 changes: 60 additions & 8 deletions src/dcps/participant.zig
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,28 @@ const IncompatQosNotify = struct {
const MatchedNotify = struct {
ctx: *anyopaque,
notify: *const fn (ctx: *anyopaque, remote_handle: DDS.InstanceHandle_t, added: bool) void,
/// `ctx` is a `*DataReaderImpl` when true, a `*DataWriterImpl` when false —
/// needed to reach the right `EntityQuiesce` from `onReaderDiscovered` /
/// `onWriterDiscovered`, which release `participant.mu` before firing
/// `notify` and so must pin the target entity themselves (a raw `ctx` is
/// otherwise free to dangle in that window — see `entity_quiesce.zig`).
is_reader: bool,

/// Call while holding `participant.mu` (so `ctx` is provably still live);
/// hold the reference until after `notify` returns, then `quiesceRelease`.
/// False = the target entity is tearing down; skip the notify entirely.
fn quiesceAcquire(self: MatchedNotify) bool {
return if (self.is_reader)
reader_mod.DataReaderImpl.quiesceAcquireFn(self.ctx)
else
writer_mod.DataWriterImpl.quiesceAcquireFn(self.ctx);
}
fn quiesceRelease(self: MatchedNotify) void {
if (self.is_reader)
reader_mod.DataReaderImpl.quiesceReleaseFn(self.ctx)
else
writer_mod.DataWriterImpl.quiesceReleaseFn(self.ctx);
}
};

/// Callback registered by DataWriterImpl / DataReaderImpl so that the participant
Expand Down Expand Up @@ -2215,6 +2237,7 @@ pub const DomainParticipantImpl = struct {
proto: proto.ProtocolReader,
info: proto.MatchedWriterInfo,
notify: ?MatchedNotify,
notify_quiesced: bool = false,
};

fn onWriterDiscovered(ctx: *anyopaque, data: *const disc.WriterData) void {
Expand Down Expand Up @@ -2270,7 +2293,11 @@ pub const DomainParticipantImpl = struct {
// races it; see protocol/interface.zig's quiesce_acquire doc.
if (!ar.proto.quiesceAcquire()) continue;
const info = buildMatchedWriterInfo(data.guid, data.qos, data.unicast_locators, data.multicast_locators);
jobs.append(self.alloc, .{ .proto = ar.proto, .info = info, .notify = ar.matched_notify }) catch ar.proto.quiesceRelease();
const nq = if (ar.matched_notify) |cb| cb.quiesceAcquire() else false;
jobs.append(self.alloc, .{ .proto = ar.proto, .info = info, .notify = ar.matched_notify, .notify_quiesced = nq }) catch {
ar.proto.quiesceRelease();
if (nq) ar.matched_notify.?.quiesceRelease();
};
}
upsertDiscoveredWriter(self, data);
if (self.builtin_sub) |bs| push_dr = bs.pub_dr;
Expand Down Expand Up @@ -2308,7 +2335,10 @@ pub const DomainParticipantImpl = struct {
self.mu.unlock();
for (jobs.items) |job| {
job.proto.addMatchedWriter(&job.info) catch {};
if (job.notify) |cb| cb.notify(cb.ctx, writer_mod.guidToHandle(data.guid), true);
if (job.notify) |cb| if (job.notify_quiesced) {
cb.notify(cb.ctx, writer_mod.guidToHandle(data.guid), true);
cb.quiesceRelease();
};
job.proto.quiesceRelease();
}
if (push_dr) |dr| pushBuiltinPublicationCdr(self.alloc, dr, data);
Expand Down Expand Up @@ -2379,6 +2409,7 @@ pub const DomainParticipantImpl = struct {
proto: proto.ProtocolWriter,
info: proto.MatchedReaderInfo,
notify: ?MatchedNotify,
notify_quiesced: bool = false,
};

fn onReaderDiscovered(ctx: *anyopaque, data: *const disc.ReaderData) void {
Expand Down Expand Up @@ -2427,7 +2458,11 @@ pub const DomainParticipantImpl = struct {
// until after self.mu is released below.
if (!aw.proto.quiesceAcquire()) continue;
const info = self.buildMatchedReaderInfo(data.guid, data.qos, data.unicast_locators, data.multicast_locators);
jobs.append(self.alloc, .{ .proto = aw.proto, .info = info, .notify = aw.matched_notify }) catch aw.proto.quiesceRelease();
const nq = if (aw.matched_notify) |cb| cb.quiesceAcquire() else false;
jobs.append(self.alloc, .{ .proto = aw.proto, .info = info, .notify = aw.matched_notify, .notify_quiesced = nq }) catch {
aw.proto.quiesceRelease();
if (nq) aw.matched_notify.?.quiesceRelease();
};
}
upsertDiscoveredReader(self, data);
if (self.builtin_sub) |bs| push_dr = bs.sub_dr;
Expand Down Expand Up @@ -2465,7 +2500,10 @@ pub const DomainParticipantImpl = struct {
self.mu.unlock();
for (jobs.items) |job| {
job.proto.addMatchedReader(&job.info) catch {};
if (job.notify) |cb| cb.notify(cb.ctx, writer_mod.guidToHandle(data.guid), true);
if (job.notify) |cb| if (job.notify_quiesced) {
cb.notify(cb.ctx, writer_mod.guidToHandle(data.guid), true);
cb.quiesceRelease();
};
job.proto.quiesceRelease();
}
if (push_dr) |dr| pushBuiltinSubscriptionCdr(self.alloc, dr, data);
Expand Down Expand Up @@ -2660,6 +2698,7 @@ pub const DomainParticipantImpl = struct {
proto: proto.ProtocolWriter,
info: proto.MatchedReaderInfo,
notify: ?MatchedNotify,
notify_quiesced: bool = false,
remote_guid: Guid,
unicast_locs: []const Locator,
multicast_locs: []const Locator,
Expand Down Expand Up @@ -2721,15 +2760,18 @@ pub const DomainParticipantImpl = struct {
const uloc = dupeLocators(self.alloc, dr.unicast_locators);
const mloc = dupeLocators(self.alloc, dr.multicast_locators);
const info = self.buildMatchedReaderInfo(dr.guid, dr.qos, uloc, mloc);
const nq = if (aw.matched_notify) |cb| cb.quiesceAcquire() else false;
jobs.append(self.alloc, .{
.proto = aw.proto,
.info = info,
.notify = aw.matched_notify,
.notify_quiesced = nq,
.remote_guid = dr.guid,
.unicast_locs = uloc,
.multicast_locs = mloc,
}) catch {
aw.proto.quiesceRelease();
if (nq) aw.matched_notify.?.quiesceRelease();
self.alloc.free(uloc);
self.alloc.free(mloc);
};
Expand All @@ -2740,7 +2782,10 @@ pub const DomainParticipantImpl = struct {
}
for (jobs.items) |job| {
job.proto.addMatchedReader(&job.info) catch {};
if (job.notify) |cb| cb.notify(cb.ctx, writer_mod.guidToHandle(job.remote_guid), true);
if (job.notify) |cb| if (job.notify_quiesced) {
cb.notify(cb.ctx, writer_mod.guidToHandle(job.remote_guid), true);
cb.quiesceRelease();
};
job.proto.quiesceRelease();
self.alloc.free(job.unicast_locs);
self.alloc.free(job.multicast_locs);
Expand Down Expand Up @@ -2789,6 +2834,7 @@ pub const DomainParticipantImpl = struct {
proto: proto.ProtocolReader,
info: proto.MatchedWriterInfo,
notify: ?MatchedNotify,
notify_quiesced: bool = false,
remote_guid: Guid,
unicast_locs: []const Locator,
multicast_locs: []const Locator,
Expand Down Expand Up @@ -2859,15 +2905,18 @@ pub const DomainParticipantImpl = struct {
const uloc = dupeLocators(self.alloc, dw.unicast_locators);
const mloc = dupeLocators(self.alloc, dw.multicast_locators);
const info = buildMatchedWriterInfo(dw.guid, dw.qos, uloc, mloc);
const nq = if (ar.matched_notify) |cb| cb.quiesceAcquire() else false;
jobs.append(self.alloc, .{
.proto = ar.proto,
.info = info,
.notify = ar.matched_notify,
.notify_quiesced = nq,
.remote_guid = dw.guid,
.unicast_locs = uloc,
.multicast_locs = mloc,
}) catch {
ar.proto.quiesceRelease();
if (nq) ar.matched_notify.?.quiesceRelease();
self.alloc.free(uloc);
self.alloc.free(mloc);
};
Expand All @@ -2878,7 +2927,10 @@ pub const DomainParticipantImpl = struct {
}
for (jobs.items) |job| {
job.proto.addMatchedWriter(&job.info) catch {};
if (job.notify) |cb| cb.notify(cb.ctx, writer_mod.guidToHandle(job.remote_guid), true);
if (job.notify) |cb| if (job.notify_quiesced) {
cb.notify(cb.ctx, writer_mod.guidToHandle(job.remote_guid), true);
cb.quiesceRelease();
};
job.proto.quiesceRelease();
self.alloc.free(job.unicast_locs);
self.alloc.free(job.multicast_locs);
Expand Down Expand Up @@ -2932,7 +2984,7 @@ pub const DomainParticipantImpl = struct {
var aw_it5 = self.active_writers.valueIterator();
while (aw_it5.next()) |aw| {
if (aw.handle == handle) {
aw.matched_notify = .{ .ctx = notify_ctx, .notify = notify_fn };
aw.matched_notify = .{ .ctx = notify_ctx, .notify = notify_fn, .is_reader = false };
break;
}
}
Expand Down Expand Up @@ -2968,7 +3020,7 @@ pub const DomainParticipantImpl = struct {
var ar_it5 = self.active_readers.valueIterator();
while (ar_it5.next()) |ar| {
if (ar.handle == handle) {
ar.matched_notify = .{ .ctx = notify_ctx, .notify = notify_fn };
ar.matched_notify = .{ .ctx = notify_ctx, .notify = notify_fn, .is_reader = true };
break;
}
}
Expand Down
Loading
Loading