paavohtl Claude Opus 4.6 (1M context) commited on
Commit
c090ef4
·
1 Parent(s): df6d1b6

fix: broadcast_message now works — use read_volatile for children state

Browse files

The pure Rust CTask::HandleMessage port (broadcast_message) was failing
because LLVM cached children_watermark and children_data reads across
virtual dispatch calls. Child handlers can modify the parent's children
array (add/remove/realloc), so these fields must be re-read each
iteration.

Fix: use read_volatile via core::ptr::addr_of! for both fields.
This matches the original WA code which re-reads [EDI+0x10] and
[EDI+0x14] from memory each iteration.

CTaskCloud and CTaskFilter handlers now use broadcast_message directly
instead of calling the original WA CTask::HandleMessage at 0x562F30.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

crates/openwa-core/src/task/base.rs CHANGED
@@ -208,49 +208,46 @@ pub unsafe trait Task {
208
  /// # Safety
209
  /// All non-null children must be valid CTask pointers with valid vtables.
210
  unsafe fn broadcast_message(
211
- &self,
212
  sender: *mut CTask,
213
  msg_type: u32,
214
  size: u32,
215
  data: *const u8,
216
  ) {
217
- let task_ptr = self.as_task_ptr();
218
-
219
- // Find the first non-null child
 
 
 
 
 
 
 
220
  let mut i: usize = 0;
221
- let mut child = core::ptr::null_mut::<CTask>();
222
  loop {
223
- let watermark = (*task_ptr).children_watermark as usize;
224
- if i >= watermark {
225
- break;
226
- }
227
- child = *(*task_ptr).children_data.add(i);
228
- i += 1;
229
- if !child.is_null() {
230
- break;
231
- }
232
- }
233
-
234
- while !child.is_null() {
235
- // Dispatch via typed CTaskVtable — every task's vtable starts
236
- // with the same 8-slot layout, so this cast is always valid.
237
- let vt = &*((*child).vtable as *const CTaskVtable);
238
- (vt.handle_message)(child, sender, msg_type, size, data);
239
-
240
- // Find next non-null child — re-read watermark each time
241
- // because the handler may have added or removed siblings
242
- child = core::ptr::null_mut();
243
- loop {
244
- let watermark = (*task_ptr).children_watermark as usize;
245
  if i >= watermark {
246
- break;
247
  }
248
- child = *(*task_ptr).children_data.add(i);
 
 
 
249
  i += 1;
250
- if !child.is_null() {
251
- break;
252
  }
253
- }
 
 
 
 
 
254
  }
255
  }
256
  }
 
208
  /// # Safety
209
  /// All non-null children must be valid CTask pointers with valid vtables.
210
  unsafe fn broadcast_message(
211
+ &mut self,
212
  sender: *mut CTask,
213
  msg_type: u32,
214
  size: u32,
215
  data: *const u8,
216
  ) {
217
+ let task_ptr = self.as_task_ptr_mut();
218
+
219
+ // Scan for non-null children and dispatch HandleMessage.
220
+ // Mirrors WA's CTask::HandleMessage at 0x562F30 exactly:
221
+ // scan → dispatch → re-read watermark → scan next → ...
222
+ //
223
+ // IMPORTANT: read_volatile is required for watermark and children_data
224
+ // because child handlers may modify this task's children array
225
+ // (add/remove children, realloc the array). LLVM would otherwise cache
226
+ // these reads across the virtual dispatch call.
227
  let mut i: usize = 0;
 
228
  loop {
229
+ // Scan for next non-null child
230
+ let child = loop {
231
+ let watermark = core::ptr::read_volatile(
232
+ core::ptr::addr_of!((*task_ptr).children_watermark),
233
+ ) as usize;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  if i >= watermark {
235
+ return;
236
  }
237
+ let children = core::ptr::read_volatile(
238
+ core::ptr::addr_of!((*task_ptr).children_data),
239
+ );
240
+ let c = *children.add(i);
241
  i += 1;
242
+ if !c.is_null() {
243
+ break c;
244
  }
245
+ };
246
+
247
+ // Dispatch via CTaskVtable — every task's vtable starts with
248
+ // the same 8-slot base layout, so this cast is always valid.
249
+ let vt = &*((*child).vtable as *const CTaskVtable);
250
+ (vt.handle_message)(child, sender, msg_type, size, data);
251
  }
252
  }
253
  }
crates/openwa-wormkit/src/replacements/task/cloud.rs CHANGED
@@ -96,10 +96,8 @@ unsafe extern "thiscall" fn cloud_handle_message(
96
  _ => {}
97
  }
98
 
99
- // Broadcast to children via original WA CTask::HandleMessage
100
- let base_handler: unsafe extern "thiscall" fn(*mut CTask, *mut CTask, u32, u32, *const u8) =
101
- core::mem::transmute(openwa_core::rebase::rb(va::CTASK_VT2_HANDLE_MESSAGE) as usize);
102
- base_handler(cloud.as_task_ptr_mut(), sender, msg_type, size, data);
103
  }
104
 
105
  pub fn install() -> Result<(), String> {
 
96
  _ => {}
97
  }
98
 
99
+ // Broadcast to children (pure Rust port of CTask::HandleMessage)
100
+ cloud.broadcast_message(sender, msg_type, size, data);
 
 
101
  }
102
 
103
  pub fn install() -> Result<(), String> {
crates/openwa-wormkit/src/replacements/task/filter.rs CHANGED
@@ -29,10 +29,8 @@ unsafe extern "thiscall" fn filter_handle_message(
29
  return; // message not subscribed — drop silently
30
  }
31
 
32
- // Broadcast to children via original WA CTask::HandleMessage
33
- let base_handler: unsafe extern "thiscall" fn(*mut CTask, *mut CTask, u32, u32, *const u8) =
34
- core::mem::transmute(openwa_core::rebase::rb(va::CTASK_VT2_HANDLE_MESSAGE) as usize);
35
- base_handler(filter.as_task_ptr() as *mut CTask, sender, msg_type, size, data);
36
  }
37
 
38
  pub fn install() -> Result<(), String> {
 
29
  return; // message not subscribed — drop silently
30
  }
31
 
32
+ // Broadcast to children (pure Rust port of CTask::HandleMessage)
33
+ (*this).broadcast_message(sender, msg_type, size, data);
 
 
34
  }
35
 
36
  pub fn install() -> Result<(), String> {