pub struct RecordInstance {Show 15 fields
pub name: String,
pub record: Box<dyn Record>,
pub common: CommonFields,
pub subscribers: HashMap<String, Vec<Subscriber>>,
pub parsed_inp: ParsedLink,
pub parsed_out: ParsedLink,
pub parsed_flnk: ParsedLink,
pub parsed_sdis: ParsedLink,
pub parsed_tsel: ParsedLink,
pub device: Option<Box<dyn DeviceSupport>>,
pub subroutine: Option<Arc<SubroutineFn>>,
pub notify: Option<Arc<NotifyWaitSet>>,
pub reprocess_generation: Arc<AtomicU64>,
pub watchdog_generation: Arc<AtomicU64>,
pub info: HashMap<String, String>,
/* private fields */
}Expand description
A type-erased record instance stored in the database.
Fields§
§name: String§record: Box<dyn Record>§common: CommonFields§subscribers: HashMap<String, Vec<Subscriber>>§parsed_inp: ParsedLink§parsed_out: ParsedLink§parsed_flnk: ParsedLink§parsed_sdis: ParsedLink§parsed_tsel: ParsedLink§device: Option<Box<dyn DeviceSupport>>§subroutine: Option<Arc<SubroutineFn>>§notify: Option<Arc<NotifyWaitSet>>§reprocess_generation: Arc<AtomicU64>Generation counter for ReprocessAfter timer cancellation. Bumped each process cycle. Spawned timers check this to avoid stale re-processes from accumulated timers.
watchdog_generation: Arc<AtomicU64>Generation counter for the monitor watchdog
(Record::watchdog_interval / Record::watchdog_fire), bumped by
each PvDatabase::arm_watchdog so a re-arm supersedes the tick already
in flight — C callbackRequestDelayed replacing an outstanding delayed
callback. Deliberately NOT reprocess_generation: C’s histogram wdog is
its own epicsCallback, independent of the record’s SDLY/async
re-entry, so an SDLY defer must not cancel the watchdog nor vice versa.
info: HashMap<String, String>Per-record info tags from info("key", "value") directives in
the .db file (epics-base info(…) grammar). Consumers include
asyn (asyn:READBACK), record-as-PV bridge tags
(Q:group, Q:form), and IOC-specific extensions. Empty for
records loaded without info(…) clauses.
Implementations§
Source§impl RecordInstance
impl RecordInstance
pub fn new(name: String, record: impl Record) -> Self
pub fn new_boxed(name: String, record: Box<dyn Record>) -> Self
Sourcepub fn soft_output_value(&self) -> Option<Option<EpicsValue>>
pub fn soft_output_value(&self) -> Option<Option<EpicsValue>>
SINGLE OWNER of the DTYP -> soft-output-dset mapping. The dset table decides what a soft OUT-link write carries; no caller may re-derive it.
C ships two soft output dsets per output record type and DTYP picks one:
devXxxSoft.c::write_xxx puts VAL/OVAL on the OUT link, while
devXxxSoftRaw.c::write_xxx puts the RAW word — dbPutLink(&prec->out, DBR_LONG, &prec->rval, 1) (devAoSoftRaw.c:44, devBoSoftRaw.c:65) or
data = prec->rval & prec->mask (devMbboSoftRaw.c:71-75,
devMbboDirectSoftRaw.c:71-75).
Record::raw_soft_output_value IS the SoftRaw column of that table:
Some exactly for the record types C ships a SoftRaw dset for. A record
type C has no SoftRaw dset for keeps the plain soft-channel value —
DTYP="Raw Soft Channel" on a longout is a .db error C rejects at
init (“no device support”), and the port’s lenient reading of it (the
same one crate::server::device_support::is_soft_dtyp already applies
on the input side) must not turn the write into a silent no-op.
None means DTYP names device support that owns the write — real
hardware, or “Async Soft Channel”, whose dset is a registered async
device.
Sourcepub fn set_info(&mut self, key: impl Into<String>, value: impl Into<String>)
pub fn set_info(&mut self, key: impl Into<String>, value: impl Into<String>)
Set a single info("key", "value") tag on this record. Last
write wins. Used by the .db loader (info(...) directive) and
dbpf-style tools.
Sourcepub fn get_info(&self, key: &str) -> Option<&str>
pub fn get_info(&self, key: &str) -> Option<&str>
Look up a single info tag. Returns None when the record has
no tag with that key.
Sourcepub fn invalidate_metadata_cache(&self)
pub fn invalidate_metadata_cache(&self)
Invalidate the metadata cache. Called after writing any metadata-class field (EGU, PREC, HOPR/LOPR, alarm limits, DRVH/DRVL, enum strings). The next snapshot will rebuild the cache from the new values.
Sourcepub fn notify_field_written(&self, field: &str)
pub fn notify_field_written(&self, field: &str)
Hook called by the database after a field is written. If the field is in the metadata-class set, the cache is invalidated so the next snapshot picks up the new value.
Field name is automatically uppercased.
Sourcepub fn notify_field_written_if_changed(
&mut self,
field: &str,
prev: Option<&EpicsValue>,
)
pub fn notify_field_written_if_changed( &mut self, field: &str, prev: Option<&EpicsValue>, )
Like [notify_field_written] but skips the invalidation when
the put did not actually change the field’s value. Mirrors
epics-base faac1df1 — DBE_PROPERTY events fire only on
real changes, not on idempotent writes (the C path compares
paddr->pfield against the converted payload before setting
the propertyUpdate flag).
prev is the value captured BEFORE the put. Callers that
don’t need the change-detection (e.g. internal writers that
know the field is non-metadata) can keep using
[notify_field_written].
Sourcepub fn is_no_mod(&self, field: &str) -> bool
pub fn is_no_mod(&self, field: &str) -> bool
C dbChannelSpecial(chan) == SPC_NOMOD — the single owner of the
no-modify declaration, for every consumer that needs to know whether a
field can be written.
C declares it once, in the .dbd, and reads it in two unrelated places:
dbPut(dbAccess.c:123-126, viadbPutSpecial(paddr, 0)) refuses the write — the port’scheck_no_modgate;rsrvCheckPut(rsrv/camessage.c:2540-2551) —if (dbChannelSpecial(pciu->dbch) == SPC_NOMOD) return 0;— which feeds the CAACCESS_RIGHTSwrite bit (camessage.c:1123-1124) as well as both put paths, so a client seesAccess: read, no writeand never sends the doomed write.
Only the first consumer existed in the port, so every dbCommon NOMOD
field advertised WRITE on the wire (caput N1.SEVR 2 was refused
server-side, after the client had already sent it, with an async
exception instead of C’s clean client-side “Write access denied”).
Three sources, one answer:
- the dbCommon
SPC_NOMODset below — common fields, so no record’sfield_listdeclares them; - the record type’s declaration, resolved by
Self::field_desc— the vendored.dbdwhenever one exists, and only for a record type that has no.dbdat all (motor,optics,scaler,std) the record’s own hand-written table, which for those Tier 3 types genuinely is their declaration; Record::field_no_mod— an SPC_NOMOD a record’scvt_dbaddrraises from its own state (compress VAL under BALG=LIFO,compressRecord.c:398-407), which a staticFieldDesccannot express.
field may be any case.
Sourcepub fn is_processing(&self) -> bool
pub fn is_processing(&self) -> bool
Check if the record is currently processing (PACT equivalent).
Sourcepub fn enter_pact(&self)
pub fn enter_pact(&self)
C prec->pact = TRUE — the record goes busy for an async device
round-trip, an SDLY simulation defer, or an ODLY reprocess window.
Sourcepub fn leave_pact(&mut self) -> PactExit
pub fn leave_pact(&mut self) -> PactExit
C prec->pact = FALSE — the ONLY release of PACT.
Every PACT→idle transition takes the put-notify parked on that window
with it (C dbNotifyCompletion, reached from recGblFwdLink on every
path that ends a cycle). The returned PactExit is #[must_use], so
a release site cannot strand the deferral the way the open-coded
processing.store(false) at the ODLY continuation and the three SIM/SDLY
releases did.
Sourcepub fn put_notify_busy(&self) -> bool
pub fn put_notify_busy(&self) -> bool
True when a put-notify already owns this record — an in-flight wait-set
(C precord->ppn) or a park from a previous PACT arm. C
processNotifyCommon (dbNotify.c:213-217) queues a second notify on the
record’s restart list; the port refuses it (S_db_Blocked /
ECA_PUTCBINPROG) — see Self::park_notify_put.
Sourcepub fn park_notify_put(
&mut self,
put: DeferredNotifyPut,
) -> Result<(), DeferredNotifyPut>
pub fn park_notify_put( &mut self, put: DeferredNotifyPut, ) -> Result<(), DeferredNotifyPut>
Park a put-notify that landed on a PACT record (C processNotifyCommon,
dbNotify.c:225-231). Err(put) hands the put back when another
put-notify already owns the record.
The PactExit invariant — deferred_notify_put.is_some() ⟹ PACT —
is established here: this is the only park, and it is only reachable
from the caller’s is_processing() arm.
Sourcepub fn resolve_field(&self, name: &str) -> Option<EpicsValue>
pub fn resolve_field(&self, name: &str) -> Option<EpicsValue>
Unified field resolution: record fields → common fields → virtual fields.
Sourcepub fn resolve_string_view_field(&self, name: &str) -> Option<EpicsValue>
pub fn resolve_string_view_field(&self, name: &str) -> Option<EpicsValue>
Resolve a field for EPICS $ long-string (character-array) access.
The $ channel-name modifier (C dbChannel.c:486-505) re-views a
field as a DBR_CHAR array: a DBF_STRING field becomes a char
array of field_size elements, a link field a char array of
PVLINK_STRINGSZ, and every other field type is rejected with
S_dbLib_fieldNotFound. pvxs serves that char view as a
form = "String" long-string NTScalar — it reads the DBR_CHAR
bytes and NUL-terminates them back into a string
(ioc/iocsource.cpp:133-136, ioc/channel.cpp:62-74).
Both DBF_STRING fields and link fields resolve to an
EpicsValue::String in this database (a link resolves to its
textual form, see Self::get_common_field), so a field is
$-eligible exactly when it resolves to a string value. Returns
that string value for an eligible field, or None for a field the
$ modifier cannot view as a char array (the
S_dbLib_fieldNotFound case) — the single owner of the
dbChannel $-eligibility rule for the channel-resolution layer.
Sourcepub fn declared_field_type(&self, field: &str) -> Option<DbFieldType>
pub fn declared_field_type(&self, field: &str) -> Option<DbFieldType>
The DBF_* type field is SERVED as — the single source of truth for
the type on the wire, on every delivery path.
This is the field’s DECLARED type (FieldDesc::dbf_type, from the
.dbd), not the type of whatever variant the record happens to store.
C resolves a channel’s field_type from the dbFldDes at
name-resolution time (dbChannelCreate -> dbNameToAddr,
dbAccess.c:184-205) and every later dbGet/db_post_events converts
the stored bytes to it — the storage is private to the record, the
declaration is the contract.
Two answers are NOT the declaration:
- a
FieldDesc::runtime_typedfield — C’scvt_dbaddroverwritespaddr->field_typefrom record state (FTVL,FTA,SDEF), and this port’scvt_dbaddris the variant the record stores; - a field with no
FieldDescat all (a virtual field).
In both cases the value’s own type is the answer, so this returns
None and Self::project_to_declared_type leaves the value alone.
Sourcepub fn project_to_declared_type(
&self,
field: &str,
value: EpicsValue,
) -> EpicsValue
pub fn project_to_declared_type( &self, field: &str, value: EpicsValue, ) -> EpicsValue
Project a field’s stored value onto its declared type
(Self::declared_field_type) — the single owner of “what type this
field goes on the wire as”, run by the CA create-channel path
(Self::client_field_value), the GET path
(Self::snapshot_for_field) and the MONITOR path
(Self::make_monitor_snapshot), so all three announce and serve the
same type.
The projection is EpicsValue::convert_to, the one value-coercion
owner — the same routine dbGet converts through. Never re-derive a
conversion here: C picks its routine from BOTH the source and the
destination type, and only convert_to knows that table.
Idempotent: a value already of its declared type is short-circuited by
convert_to, and re-projecting a projected value is a no-op. That is
what lets the CA path derive the native type from the value it is about
to serve.
Sourcepub fn client_field_value(&self, field: &str) -> Option<EpicsValue>
pub fn client_field_value(&self, field: &str) -> Option<EpicsValue>
The client-facing value of field: the resolved value projected onto
the field’s declared type (Self::project_to_declared_type), so a
native type derived from the value — which is what the CA
create-channel path does — is the DECLARED type, and matches the
GET/MONITOR data byte for byte.
Sourcepub fn snapshot_for_field(&self, field: &str) -> Option<Snapshot>
pub fn snapshot_for_field(&self, field: &str) -> Option<Snapshot>
Build a Snapshot with full metadata for the given field.
Sourcepub fn property_support_for_field(&self, field: &str) -> PropertySupport
pub fn property_support_for_field(&self, field: &str) -> PropertySupport
The property mask a channel on field supplies, without building a
snapshot — what a PVA server needs to decide which NT leaves it may
MARK for a channel it has not read yet (QSRV resolves a group’s member
masks once, at monitor start, rather than per event).
Same two gates, same owner as Self::assign_property_support: an
unknown field supplies nothing.
Sourcepub fn get_common_field(&self, name: &str) -> Option<EpicsValue>
pub fn get_common_field(&self, name: &str) -> Option<EpicsValue>
Get a common field value.
Sourcepub fn put_common_field(
&mut self,
name: &str,
value: EpicsValue,
) -> CaResult<CommonFieldPutResult>
pub fn put_common_field( &mut self, name: &str, value: EpicsValue, ) -> CaResult<CommonFieldPutResult>
Set a common field value from a runtime dbPut (CA/PVA/dbpf/link).
Returns what scan index changes are needed.
A DBF_MENU common field’s string is converted by C’s runtime
converter, dbConvert.c::putStringMenu — see [MenuBound::DbPut].
Sourcepub fn set_scan(&mut self, new_scan: ScanType) -> CommonFieldPutResult
pub fn set_scan(&mut self, new_scan: ScanType) -> CommonFieldPutResult
The single owner of a record’s SCAN transition — C dbPutField on
SCAN, which is scanDelete(precord) … scanAdd(precord)
(dbAccess.c::dbPutSpecial SPC_SCAN, dbScan.c:236-248).
Two callers reach it, and they are the two C sites that move a record
between scan lists: a SCAN put (Self::put_common_field) and the
simulation-mode scan swap (recGblCheckSimm, recGbl.c:427-437, which
calls exactly the same scanDelete/scanAdd pair). Returns the delta
for the scan-index owner (PvDatabase::update_scan_index) to apply once
the record lock is down; CommonFieldPutResult::NoChange when the scan
did not move.
Sourcepub fn rec_gbl_save_simm(&mut self)
pub fn rec_gbl_save_simm(&mut self)
C recGblSaveSimm (recGbl.c:421-425) — latch the CURRENT simulation
mode into OLDSIMM:
void recGblSaveSimm(const epicsEnum16 sscn,
epicsEnum16 *poldsimm, const epicsEnum16 simm) {
if (sscn == USHRT_MAX) return;
*poldsimm = simm;
}The only writer of CommonFields::oldsimm. Must run BEFORE the SIMM
value moves — C calls it from special(SPC_MOD) pass 0 (before the put)
and from recGblGetSimm/recGblInitSimm before the SIML read. The
sscn == 65535 guard is C’s: with SSCN unset there is no scan to swap
to, so the latch is not even taken (and Self::rec_gbl_check_simm
bails on the same test, so the stale OLDSIMM is never read).
A record type with no SSCN/OLDSIMM in its C dbd (busy, swait) passes
neither pointer to any recGbl helper: no-op here.
Sourcepub fn rec_gbl_check_simm(&mut self) -> CommonFieldPutResult
pub fn rec_gbl_check_simm(&mut self) -> CommonFieldPutResult
C recGblCheckSimm (recGbl.c:427-437) — on a SIMM transition, swap the
record’s SCAN with SSCN:
void recGblCheckSimm(struct dbCommon *pcommon, epicsEnum16 *psscn,
const epicsEnum16 oldsimm, const epicsEnum16 simm) {
if (*psscn == USHRT_MAX) return;
if (simm != oldsimm) {
epicsUInt16 scan = pcommon->scan;
scanDelete(pcommon);
pcommon->scan = *psscn;
scanAdd(pcommon);
*psscn = scan;
}
}This is what makes SSCN mean anything at all: a record configured
field(SCAN,"1 second") field(SSCN,"Passive") stops periodic scanning
the moment SIMM leaves NO, and resumes it when SIMM goes back — with the
two fields having traded places each time. Both are a genuine swap, not
an assignment: SSCN ends up holding the scan the record just left.
The only writer of the SIMM-driven SCAN/SSCN swap. The scan-list
move itself goes through the single SCAN owner Self::set_scan, whose
CommonFieldPutResult the caller hands to
PvDatabase::update_scan_index once the record lock is down. Runs AFTER
the SIMM value moved — C special(SPC_MOD) pass 1, and the tail of
recGblGetSimm/recGblInitSimm.
Sourcepub fn put_ackt(&mut self, value: u16)
pub fn put_ackt(&mut self, value: u16)
C dbAccess.c::putAckt (:1285-1300) — the only writer of ACKT.
Reached from dbPut for a DBR_PUT_ACKT request type
(dbAccess.c:1331-1332), ABOVE the SPC_NOMOD gate that refuses every
ordinary put to the field. Posts exactly what C posts: the ACKT change,
the ACKS it may lower, and the record-wide DBE_ALARM — and only when
ackt actually changed (C returns 0 early otherwise).
Sourcepub fn put_acks(&mut self, value: u16)
pub fn put_acks(&mut self, value: u16)
C dbAccess.c::putAcks (:1302-1315) — the only runtime writer of
ACKS. Reached from dbPut for a DBR_PUT_ACKS request type, ABOVE the
SPC_NOMOD gate.
The acknowledged severity is compared against the STORED unacknowledged
severity acks, not the current sevr: an operator acknowledging at
the severity that was latched into ACKS clears it even after sevr has
since dropped. A too-low acknowledgement changes nothing and posts
nothing; an acknowledgement of an already-clear ACKS still posts, which
is C’s literal if (*psev >= precord->acks) (0 >= 0 holds).
Sourcepub fn put_common_field_db_load(
&mut self,
name: &str,
value: EpicsValue,
) -> CaResult<CommonFieldPutResult>
pub fn put_common_field_db_load( &mut self, name: &str, value: EpicsValue, ) -> CaResult<CommonFieldPutResult>
Set a common field value from the .db loader, which in C is a
different converter with a different out-of-menu bound
(dbStaticRun.c::dbPutStringNum; see [MenuBound::DbLoad]). It is what
lets field(SSCN,"65535") — the menuScan “use SCAN” sentinel, out of
the menu’s 0-9 range — load, while caput REC.SSCN 65535 is refused at
runtime exactly as C refuses it.
Sourcepub fn get_virtual_field(&self, name: &str) -> Option<EpicsValue>
pub fn get_virtual_field(&self, name: &str) -> Option<EpicsValue>
Get virtual fields (NAME, RTYP).
Sourcepub fn evaluate_alarms(&mut self)
pub fn evaluate_alarms(&mut self)
Evaluate alarms based on record type and current value. Uses rec_gbl_set_sevr to accumulate into nsta/nsev.
CALC_ALARM is NOT raised here. C raises it inside the record’s own
process() (calcRecord.c:121-123, calcoutRecord.c:238-241,
sCalcoutRecord.c:357-363, aCalcoutRecord.c:304-305,
swaitRecord.c:409-410), and in the port Record::check_alarms — which
runs immediately before this — is that owner. It used to be raised here
instead, keyed on a hardcoded rtype list plus a CALC_ALARM pseudo-field
no DBD declares; swait is what that construction cost: it carried the flag
but was not on the list, so a failed calcPerform alarmed nowhere.
Sourcepub fn process_local(
&mut self,
) -> CaResult<(ProcessSnapshot, Vec<(&'static str, EventMask)>)>
pub fn process_local( &mut self, ) -> CaResult<(ProcessSnapshot, Vec<(&'static str, EventMask)>)>
Basic process: process record, evaluate alarms, timestamp, build snapshot. This does NOT handle links — see process_with_context in database.rs.
Returns the value/log snapshot plus a list of alarm-field posts
(SEVR/STAT/AMSG/ACKS) with their individual C event masks.
SEVR is posted DBE_VALUE only; STAT/AMSG carry DBE_ALARM
(sevr/amsg change) and/or DBE_VALUE (stat change). The caller
must fire these via notify_field so a DBE_VALUE-only .SEVR
subscriber is not missed on an alarm-only change and a
DBE_ALARM-only subscriber is not wrongly notified — C parity
with recGblResetAlarms (recGbl.c:201-220), matching the
processing.rs link path.
pub fn check_deadband_ext(&mut self) -> (bool, bool)
Sourcepub fn make_monitor_snapshot(&self, field: &str, value: EpicsValue) -> Snapshot
pub fn make_monitor_snapshot(&self, field: &str, value: EpicsValue) -> Snapshot
Build a Snapshot for a given value, populated with the record’s display metadata.
Uses the metadata cache so the populate cost is paid at most once
per metadata-stable interval (cf. cached_metadata).
Sourcepub fn notify_from_snapshot(&self, snapshot: &ProcessSnapshot)
pub fn notify_from_snapshot(&self, snapshot: &ProcessSnapshot)
Notify subscribers from a snapshot (call outside lock).
Each entry carries its own posting mask: only subscribers whose
mask intersects that field’s mask are notified, and the
delivered MonitorEvent reports exactly that field’s classes
(C db_post_events(prec, &field, mask) per-field granularity).
Sourcepub fn notify_field(&mut self, field: &str, mask: EventMask)
pub fn notify_field(&mut self, field: &str, mask: EventMask)
Notify subscribers of a specific field, filtering by event mask.
Sourcepub fn notify_record_alarm(&mut self)
pub fn notify_record_alarm(&mut self)
C db_post_events(precord, NULL, DBE_ALARM): post a record-wide
alarm event. Delivers to every subscriber on any field whose mask
includes DBE_ALARM, each carrying its own monitored field’s current
value (the per-field notify_field already filters by mask
intersection). Used by the alarm-acknowledge (ACKT/ACKS) put path so
an alarm-mask monitor on any field observes the acknowledgement.
Sourcepub fn notify_field_with_origin(
&mut self,
field: &str,
mask: EventMask,
origin: u64,
)
pub fn notify_field_with_origin( &mut self, field: &str, mask: EventMask, origin: u64, )
Notify subscribers with an origin tag for self-write filtering.
This is C db_post_events(precord, pfield, mask) for one field, and —
per the last_posted contract — the poster that advances the
already-published value when mask carries a value class. Taking
&mut self is what makes that unbypassable: there is no way to publish
a field’s value through the framework without the change detector
learning that it was published.
Sourcepub fn add_subscriber(
&mut self,
field: &str,
sid: u32,
data_type: DbFieldType,
mask: u16,
) -> Option<EventReader>
pub fn add_subscriber( &mut self, field: &str, sid: u32, data_type: DbFieldType, mask: u16, ) -> Option<EventReader>
Add a subscriber for a specific field. Returns None when the
per-field subscriber cap (EPICS_CAS_MAX_SUBSCRIBERS_PER_PV)
is reached. the parallel cap on ProcessVariable
defends against a misbehaving client opening many
MONITOR ops against one shared PV; the same defence is needed
for record fields, which the CA server’s
ChannelTarget::RecordField path lands on.
Sourcepub fn add_subscriber_on(
&mut self,
user: &EventUser,
field: &str,
sid: u32,
data_type: DbFieldType,
mask: u16,
) -> Option<EventReader>
pub fn add_subscriber_on( &mut self, user: &EventUser, field: &str, sid: u32, data_type: DbFieldType, mask: u16, ) -> Option<EventReader>
Add a field subscriber whose events queue on user’s event queue —
C db_add_event with the circuit’s event_user as context. Every
subscription on one CA circuit shares that queue and therefore its
nDuplicates, so a duplicate queued for one of them releases the
EVENTS_OFF drain for all of them (dbEvent.c:947). In-process consumers
use Self::add_subscriber, which gives each its own event_user.
Sourcepub fn attach_filter_to_last_subscriber(
&mut self,
field: &str,
filter: Arc<dyn SubscriptionFilter>,
) -> bool
pub fn attach_filter_to_last_subscriber( &mut self, field: &str, filter: Arc<dyn SubscriptionFilter>, ) -> bool
Attach a filter to the most recently added subscriber for
field. Returns false when no subscriber exists yet on that
field (call add_subscriber first). The CA / PVA channel-name
parsers will use this once .{filter:opts} syntax is wired.
Tests can also use it directly to compose filter chains.
Sourcepub fn remove_subscriber(&mut self, sid: u32)
pub fn remove_subscriber(&mut self, sid: u32)
Remove a subscriber by subscription ID from all fields.
Sourcepub fn set_subscriber_active(&mut self, sid: u32, active: bool)
pub fn set_subscriber_active(&mut self, sid: u32, active: bool)
Pause / resume one subscriber’s event flow at the source
(db_event_disable / db_event_enable). active == false
suppresses every subsequent post to this subscriber, so the record stops
doing per-event work for it. Entries already queued stay queued and are
still delivered, exactly as in C: db_event_disable only unlinks the
subscription from the record’s monitor list (dbEvent.c:521-533) and
never reaches into the event queue. No-op if no subscriber has this
sid. The caller holds the record write lock, so this is exclusive with
the read-locked post paths that consult Subscriber::active.
Sourcepub fn cleanup_subscribers(&mut self)
pub fn cleanup_subscribers(&mut self)
Clean up subscriber rows whose consumer is gone.