Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Complete threads #332

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft

Complete threads #332

wants to merge 2 commits into from

Conversation

layus
Copy link
Member

@layus layus commented Nov 6, 2020

A draft implementation of thread methods still missing.

I would be glad to get feddback on the validity of thread pointers validity after thread free.
I guess this widens the door to use after free issues and segfaults (already opened with other thread methods), but not too sure.

/cc @sjrd

Based on top of #331
Fixes mozart/mozart#200

Copy link
Member

@sjrd sjrd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be glad to get feddback on the validity of thread pointers validity after thread free.
I guess this widens the door to use after free issues and segfaults (already opened with other thread methods), but not too sure.

AFAICT, this is fine. Thread instances are garbage collected like any other value. If an Oz variable holds a reference to a thread, and that variable is alive, then the Thread instance stays alive.

You might want to check that you don't try to resume/suspend a terminated Thread, though. IIUC currently those are C++ assertions, but if coming from explicit calls in Oz, which should throw proper Oz exceptions instead.

static void call(VM vm, In thread) {
Runnable* runnable = getArgument<Runnable*>(vm, thread);

runnable->suspend();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't be enough if a thread wants to suspend itself. The easiest way to deal with that might be to do the dispatch in Oz, with:

suspend: proc {$ T}
  if {Boot_Thread.this} == T then
    {Boot_Thread.preempt}
  else
    {Boot_Thread.suspend T}
  end
end

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, indeed. I do not know if suspending itself should preempt or not. I guess it makes sense, but we still have to suspend it. Suspend is like a preempt until resume is called.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hum, you're right, it should definitely suspend, not just preempt. But if we only suspend, I think the emulator loop won't notice that it needs to unschedule itself now. It's a bit tricky. Maybe the following works:

suspend: proc {$ T}
  {Boot_Thread.suspend T}
  if {Boot_Thread.this} == T then
    {Boot_Thread.preempt}
  end
end

but maybe that causes all sorts of complications (as some bytecode instructions will keep running even though the thread is marked as suspended.

It might not be possible to do this properly in Oz after all.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A thread is never "marked" as suspended. It is just removed from the active queues. suspend + preempt should be perfectly fine.

@layus
Copy link
Member Author

layus commented Nov 6, 2020

AFAICT, this is fine. Thread instances are garbage collected like any other value. If an Oz variable holds a reference to a thread, and that variable is alive, then the Thread instance stays alive.

AFAIK the Thread value in oz wraps a raw pointer to the vm thread structure. I do not think these are reference counted.

@layus
Copy link
Member Author

layus commented Nov 6, 2020

Oh, and of course I cannot test this PR because of #330

@layus
Copy link
Member Author

layus commented Nov 6, 2020

Oz has a precise semantics :-). Why does this take a thread as argument ?

preempt

   {Thread.preempt +Thread} 

preempts the current thread, i. e., immediately schedules another runnable thread (if there is one). Thread stays runnable.

@sjrd
Copy link
Member

sjrd commented Nov 6, 2020

Oz has a precise semantics :-). Why does this take a thread as argument ?

No idea 😕

@sjrd
Copy link
Member

sjrd commented Nov 6, 2020

AFAICT, this is fine. Thread instances are garbage collected like any other value. If an Oz variable holds a reference to a thread, and that variable is alive, then the Thread instance stays alive.

AFAIK the Thread value in oz wraps a raw pointer to the vm thread structure. I do not think these are reference counted.

It does wrap a pointer to the vm Thread structure, yes. But instances of Threads are themselves subject to garbage collection. Virtually everything is. You can look at how Variables also store a list of Wakeables, which typically are ReifiedThreads. When a thread suspends on an unbound variable, it adds itself to the list of the target Variable, unschedules itself, and then "disappears". The only reference left to it is in the garbage collectable list of wakeables in the Variable.

@layus
Copy link
Member Author

layus commented Nov 6, 2020

Good to know ! Thanks for the detailed explanation :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Thread.suspend produces runtime errors
2 participants