replicad icon indicating copy to clipboard operation
replicad copied to clipboard

Task cancelation

Open mordv opened this issue 2 months ago • 7 comments

Hello! I have general question about OC and replicad bindings:

The current architecture runs each worker function to the end even if we don't need the result anymore. If I remember correctly, there is a task queue with 1 element.

But let's say we want to add stl-export visualazer with tweakable params. The blob generation might take up to minutes - what if the user change his mind and adjust params? Should we wait for buildBlob to finish? Should we start a new one and ignore shape.blobSTL execution?

The only option I've found is to start a new worker (from main worker) for OC each operation we might want to cancel and kill it manually or after execution. But I'm not sure it's possible, since main worker store the data (SHAPE_MEMORY iirc) which might be either non-transferable or too large to transfer.

Have someone thought about this problem? Maybe there is some OC api that would force running shape.blobSTL (or any function) throw?

mordv avatar Nov 08 '25 12:11 mordv

highly related: exactly this question asked on oc forum by creator of oc.js Yet to understand what "The class BRepMesh_IncrementalMesh is equipped with Message_ProgressRange" means :)

mordv avatar Nov 08 '25 14:11 mordv

"The class BRepMesh_IncrementalMesh is equipped with Message_ProgressRange" means

I see what that means, but I am not sure how one would use it to stop a computation (it is supposed to report if the computation was interrupted).

I had been thinking of what you are suggesting - and kept it the way it is because it felt that the complexity was not worth it at that time. Perhaps, if your model needs minutes to rerender you might want to offer it in a different way (i.e. with a computation that does not run on every change...)

sgenoud avatar Nov 14 '25 18:11 sgenoud

Poor man’s way to do it is to just spawn a new worker and start new computation there. I’ve never checked if there’s a way to force close a running worker to save resources. In practice waiting for GC to discard it isn’t as bad as it sounds.

In Scriptcad I also pre-spawn one worker and let it load the kernel in background, so when I need it it’s already half loaded.

paulftw avatar Nov 14 '25 19:11 paulftw

Has anyone tried throwing an exception from the progress range handler? )

paulftw avatar Nov 14 '25 19:11 paulftw

you might want to offer it in a different way

Yes and no. Some atomic computations might take tens of seconds, without cancelation the time is essentially doubled. Let's say the editing took us 3 seconds, during first 1-2 seconds (depending on how we configured debounce and throttle) the computation starts, then after editing is done the other is scheduled. Therefore ok-ish 10+3 seconds turns into 20 etc. We could of course turn off auto computation, but that's a hack rather.

with a computation that does not run on every change...

And that's an interesting but separate part of the story. For example, in the build123 you might work with jupyter-style notebook and rerun a block as you wish. I don't know yet how this is accomplished nor whether that possible with js eval. I've seen example of yours from natto.dev. And that was inspirational, I think node-based editing could significantly optimize computations.

And that's takes us to the third related part: to design a decent node based system we need parallelism - otherwise unrelated nodes would be blocked by a node with heavy computation.

Summarising, there is three related architecture challenges:

  • Task cancelation
  • Caching, checkpoints etc - something to save computation so little tweaks of fillet radius wouldn't recalculate the model.
  • Parallelism.

I know this is most likely out of scope of Replicad, but let's at least have some centralised discussion started - all that points have been thought about individually already, but results are spreaded.

Poor man’s way to do it is to just spawn a new worker and start new computation there.

The problem (well, I think it will be a problem) is that there is one master worker, which stores some computation cache. From what I know from workers - they either serialize data/transfer it/or share some binary. I've not yet tested whether or not this is transferable and what the overhead would be.

In Scriptcad

Great tool! Let me ask if you are running all the tools in browser? Especially build123 - they specifically told me they won't compile into wasm.

mordv avatar Nov 15 '25 15:11 mordv

The more I learn the more depressing it gets. We can't pass AbortSignal to the worker since its not transferable. User's replicad code is sync by design, so new Function('main =') never yields - therefore the worker does not receive messages. We could leave setting yield points at user's responsibility

  const y = () => {
    await new Promise(r => setTimeout(r, 0)) // +50% compute time with that little line. But worker receives messages.
    if(flag) throw 'aborted'
  }

  for(let i = 0; i < 300; ++ i) {
    y(); // user have to mark those points.
    shape.clone().extrude(10);
  }

There is also absolute brutal way using XMLHttpRequest, but again manual.

The only option I see is to modify AST and inject awaits somewhere between api calls.

mordv avatar Nov 20 '25 15:11 mordv

Killing and creating web workers has a large overhead. I tried this and dropped the approach. I am now using 4 workers to work of a queue in parallel. This might not help with your problem when the change rate is high and the calculations take that much time.

To make it work four you, add checkpoints to abort your calculations.

kriho avatar Dec 16 '25 19:12 kriho