mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-30 08:44:37 +00:00
library/bt_array.m:
Reorder the arguments of the resize and shrink predicates
to make them easier to use with state variables.
tests/general/array_test.m:
Update the test calls to these two predicates.
library/version_array2d.m:
Rewrite part of this module using a programming style that includes
variable names that are longer than one character :-(. In the absence
of comprehensive, or even basic, test cases, leave the rest using
the old style.
Add a non-field-syntax lookup operation.
Mark the site of a probable bug.
library/version_bitmap.m:
Add non-field-syntax versions of the get_bit and set_bit operations.
NEWS.md:
Announce the reordering and the new predicates above.
library/getopt.m:
library/getopt_io.m:
Apply to getopt_io.m an update that was previously applied to getopt.m,
even though getopt.m is now computed from getopt_io.m.
library/array2d.m:
library/bit_buffer.m:
library/bit_buffer.read.m:
library/bit_buffer.write.m:
library/bitmap.m:
library/cord.m:
library/edit_distance.m:
library/edit_seq.m:
library/fat_sparse_bitset.m:
library/fatter_sparse_bitset.m:
library/list.m:
library/one_or_more.m:
library/pair.m:
library/ra_list.m:
library/rbtree.m:
library/set_bbbtree.m:
library/set_ctree234.m:
library/set_ordlist.m:
library/set_tree234.m:
library/set_unordlist.m:
library/solutions.m:
library/sparse_bitset.m:
library/test_bitset.m:
library/thread.barrier.m:
library/thread.m:
library/thread.semaphore.m:
library/time.m:
library/tree_bitset.m:
library/version_array.m:
library/version_hash_table.m:
library/version_store.m:
General updates to style, the main ones being the following.
Using standard names for type variables:
- K and V for key and value types in map-like structures
- A to F for types of accumulators
- T for most everything else, possibly with a numeric suffix
to distinguish two separate types that nevertheless play
similar roles.
(I left descriptive type var names such as Stream and Store unchanged.)
Adding or expanding descriptions of exported predicates and functions.
Declaring and defining field syntax getters and setters without using ^,
while keeping the use of field syntax, including ^, in the operations'
descriptions.
Defining field syntax operations operations in terms of the
non-field-syntax versions, not vice versa.
Defining function versions of operations in terms of the predicate
versions, not vice versa.
1254 lines
40 KiB
Mathematica
1254 lines
40 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2000-2001, 2003-2004, 2006-2008, 2010-2011 The University
|
|
% of Melbourne.
|
|
% Copyright (C) 2014-2024 The Mercury team.
|
|
% This file is distributed under the terms specified in COPYING.LIB.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: thread.m.
|
|
% Authors: conway, wangp.
|
|
% Stability: medium.
|
|
%
|
|
% This module defines the Mercury concurrency interface.
|
|
%
|
|
% The term `concurrency' refers to threads, not necessarily to parallel
|
|
% execution of those threads. (The latter is also possible if you are using
|
|
% one of the .par grades or the Java or C# backends.)
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module thread.
|
|
:- interface.
|
|
|
|
:- import_module io.
|
|
:- import_module maybe.
|
|
|
|
:- include_module barrier.
|
|
:- include_module channel.
|
|
:- include_module closeable_channel.
|
|
:- include_module future.
|
|
:- include_module mvar.
|
|
:- include_module semaphore.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Abstract type representing a detached thread.
|
|
%
|
|
:- type thread.
|
|
|
|
% Abstract type representing a joinable thread.
|
|
%
|
|
:- type joinable_thread(T).
|
|
|
|
% can_spawn succeeds if spawn/4 is supported in the current grade.
|
|
%
|
|
:- pred can_spawn is semidet.
|
|
|
|
% can_spawn_native succeeds if spawn_native/4 is supported in the current
|
|
% grade.
|
|
%
|
|
:- pred can_spawn_native is semidet.
|
|
|
|
% spawn(Closure, IO0, IO) is true iff IO0 denotes a list of I/O
|
|
% transactions that is an interleaving of those performed by Closure
|
|
% and those contained in IO - the list of transactions performed by
|
|
% the continuation of spawn/3.
|
|
%
|
|
% Operationally, spawn/3 is like spawn/4 except that Closure does not
|
|
% accept a thread handle argument, and an exception is thrown if the
|
|
% thread cannot be created.
|
|
%
|
|
:- pred spawn(pred(io, io)::in(pred(di, uo) is cc_multi),
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
% spawn(Closure, Res, IO0, IO) creates a new thread and performs Closure in
|
|
% that thread. On success it returns ok(Thread) where Thread is a handle to
|
|
% the new thread. Otherwise it returns an error.
|
|
%
|
|
:- pred spawn(pred(thread, io, io)::in(pred(in, di, uo) is cc_multi),
|
|
maybe_error(thread)::out, io::di, io::uo) is cc_multi.
|
|
|
|
% A type representing options that affect thread creation.
|
|
%
|
|
:- type thread_options.
|
|
|
|
% Create a new thread options object with options set to their default
|
|
% values. The options are:
|
|
%
|
|
% - min_stack_size: the minimum stack size in bytes (default: 0).
|
|
% The special value 0 means to use the default stack size as chosen by
|
|
% the underlying environment.
|
|
%
|
|
:- func init_thread_options = thread_options.
|
|
|
|
% Set the minimum stack size (in bytes) for a new thread created with these
|
|
% thread options. This only affects C grades that use POSIX threads.
|
|
% The Java and C# backends do not yet respect the minimum stack size
|
|
% option.
|
|
%
|
|
:- pred set_min_stack_size(uint::in, thread_options::in, thread_options::out)
|
|
is det.
|
|
|
|
% spawn_native(Closure, Res, !IO):
|
|
% Same as spawn_native(Closure, init_thread_options, Res, !IO).
|
|
%
|
|
:- pred spawn_native(pred(thread, io, io)::in(pred(in, di, uo) is cc_multi),
|
|
maybe_error(thread)::out, io::di, io::uo) is cc_multi.
|
|
|
|
% spawn_native(Closure, Options, Res, IO0, IO):
|
|
% Like spawn/4, but Closure will be performed in a separate "native thread"
|
|
% of the environment the program is running in (POSIX thread, Windows
|
|
% thread, Java thread, etc.).
|
|
%
|
|
% spawn_native exposes a low-level implementation detail, so it is more
|
|
% likely to change with the implementation.
|
|
%
|
|
% Rationale: on the low-level C backend Mercury threads are multiplexed
|
|
% onto a limited number of OS threads. A call to a blocking procedure
|
|
% prevents that OS thread from making progress on another Mercury thread.
|
|
% Also, some foreign code depends on OS thread-local state so needs to be
|
|
% consistently executed on a dedicated OS thread to be usable.
|
|
%
|
|
:- pred spawn_native(pred(thread, io, io)::in(pred(in, di, uo) is cc_multi),
|
|
thread_options::in, maybe_error(thread)::out, io::di, io::uo) is cc_multi.
|
|
|
|
% spawn_native_joinable(Closure, Options, Res, IO0, IO);
|
|
%
|
|
% Create a joinable native thread (like spawn_native), then perform Closure
|
|
% in that thread. Another thread can call join_thread/4 to wait for the
|
|
% thread to terminate, and fetch the output returned by Closure.
|
|
% The thread will continue to take up system resources until it terminates
|
|
% and has been joined by a call to join_thread/4.
|
|
%
|
|
:- pred spawn_native_joinable(
|
|
pred(joinable_thread(T), T, io, io)::in(pred(in, out, di, uo) is cc_multi),
|
|
thread_options::in, maybe_error(joinable_thread(T))::out, io::di, io::uo)
|
|
is cc_multi.
|
|
|
|
% join_thread(Thread, Res, !IO):
|
|
%
|
|
% Wait for the specified thread to terminate. If the thread has already
|
|
% terminated, join_thread/4 will return immediately. On success, Res will
|
|
% be ok(Output) where Output is the value returned by the closure
|
|
% performed on that thread.
|
|
%
|
|
% A thread must only be joined once. If multiple threads simultaneously
|
|
% try to join with the same thread, the results are undefined.
|
|
%
|
|
:- pred join_thread(joinable_thread(T)::in, maybe_error(T)::out,
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
% yield(IO0, IO) is logically equivalent to (IO = IO0) but
|
|
% operationally, yields the Mercury engine to some other thread
|
|
% if one exists.
|
|
%
|
|
% NOTE: this is not yet implemented in the hl*.par.gc grades; currently
|
|
% it is a no-op in those grades.
|
|
%
|
|
:- pred yield(io::di, io::uo) is det.
|
|
|
|
% num_processors(Num, !IO)
|
|
%
|
|
% Retrieve the number of processors available to this process for
|
|
% parallel execution, if known.
|
|
%
|
|
% Note that the number of available processors can be different from the
|
|
% actual number of processors/cores:
|
|
%
|
|
% + It includes hardware threads.
|
|
% + The Mercury grade may restrict the process to one processor.
|
|
% + The OS may be configured to restrict the number of processors
|
|
% available (e.g. cpuset(7) on Linux).
|
|
%
|
|
:- pred num_processors(maybe(int)::out, io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module bool.
|
|
:- import_module mutvar.
|
|
:- import_module require.
|
|
|
|
:- pragma foreign_decl("C", "
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
#if (!defined(MR_EXEC_TRACE) && !defined(MR_DEEP_PROFILING)) \
|
|
|| !defined(MR_USE_GCC_NONLOCAL_GOTOS)
|
|
// In calling thread.yield, semaphore.wait or semaphore.signal,
|
|
// the calling context may need to suspend and yield to another context.
|
|
// This is implemented by setting the resume address of the context to
|
|
// an auxiliary function outside of the foreign_proc. This breaks when
|
|
// execution tracing or deep profiling are enabled as code inserted at the
|
|
// end of the foreign_proc won't be executed. In those cases we rely on
|
|
// the gcc extension that allows us to take the address of labels within
|
|
// the foreign_proc, so the context will resume back inside the
|
|
// foreign_proc.
|
|
//
|
|
// XXX Implement those procedures as :- pragma external_preds so that the
|
|
// transforms won't be applied.
|
|
#define ML_THREAD_AVOID_LABEL_ADDRS
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef MR_THREAD_SAFE
|
|
typedef pthread_t ML_ThreadHandle;
|
|
#else
|
|
typedef MR_Word ML_ThreadHandle;
|
|
#endif
|
|
").
|
|
|
|
:- pragma foreign_decl("Java", "
|
|
import jmercury.runtime.JavaInternal;
|
|
import jmercury.runtime.MercuryThread;
|
|
import jmercury.runtime.Task;
|
|
").
|
|
|
|
:- type thread_options
|
|
---> thread_options(
|
|
min_stack_size :: uint
|
|
).
|
|
|
|
:- type thread
|
|
---> detached_thread(thread_desc).
|
|
|
|
:- type joinable_thread(T)
|
|
---> joinable_thread(
|
|
jt_handle :: thread_handle,
|
|
jt_mutvar :: mutvar(T)
|
|
).
|
|
|
|
% A descriptor for a (detached) Mercury thread.
|
|
% thread_desc values are not publicly exported, but they may help with
|
|
% debugging by printing and/or comparing of 'thread' values. There is
|
|
% no guarantee that a thread descriptor remains unique after a thread
|
|
% terminates, as the memory address used to derive the descriptor
|
|
% may be reused.
|
|
%
|
|
:- type thread_desc == string.
|
|
|
|
% A thread handle from the underlying thread API.
|
|
%
|
|
:- type thread_handle.
|
|
:- pragma foreign_type("C", thread_handle, "ML_ThreadHandle").
|
|
:- pragma foreign_type("C#", thread_handle, "System.Threading.Thread").
|
|
:- pragma foreign_type("Java", thread_handle,
|
|
"jmercury.runtime.MercuryThread").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
can_spawn :-
|
|
( can_spawn_context
|
|
; can_spawn_native
|
|
).
|
|
|
|
:- pred can_spawn_context is semidet.
|
|
|
|
:- pragma foreign_proc("C",
|
|
can_spawn_context,
|
|
[will_not_call_mercury, promise_pure, thread_safe, may_not_duplicate],
|
|
"
|
|
#if !defined(MR_HIGHLEVEL_CODE)
|
|
SUCCESS_INDICATOR = MR_TRUE;
|
|
#else
|
|
SUCCESS_INDICATOR = MR_FALSE;
|
|
#endif
|
|
").
|
|
:- pragma foreign_proc("Java",
|
|
can_spawn_context,
|
|
[will_not_call_mercury, promise_pure],
|
|
"
|
|
SUCCESS_INDICATOR = true;
|
|
").
|
|
|
|
can_spawn_context :-
|
|
semidet_fail.
|
|
|
|
:- pragma foreign_proc("C",
|
|
can_spawn_native,
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
#if defined(MR_THREAD_SAFE)
|
|
SUCCESS_INDICATOR = MR_TRUE;
|
|
#else
|
|
SUCCESS_INDICATOR = MR_FALSE;
|
|
#endif
|
|
").
|
|
:- pragma foreign_proc("C#",
|
|
can_spawn_native,
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
SUCCESS_INDICATOR = true;
|
|
").
|
|
:- pragma foreign_proc("Java",
|
|
can_spawn_native,
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
SUCCESS_INDICATOR = true;
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
spawn(Goal0, !IO) :-
|
|
Goal = (pred(_Thread::in, IO0::di, IO::uo) is cc_multi :- Goal0(IO0, IO)),
|
|
spawn(Goal, Res, !IO),
|
|
(
|
|
Res = ok(_)
|
|
;
|
|
Res = error(Error),
|
|
unexpected($pred, Error)
|
|
).
|
|
|
|
spawn(Goal, Res, !IO) :-
|
|
( if can_spawn_context then
|
|
spawn_context(Goal, Res, !IO)
|
|
else
|
|
spawn_native(Goal, Res, !IO)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred spawn_context(pred(thread, io, io)::in(pred(in, di, uo) is cc_multi),
|
|
maybe_error(thread)::out, io::di, io::uo) is cc_multi.
|
|
|
|
spawn_context(Goal, Res, !IO) :-
|
|
spawn_context_2(Goal, Success, ThreadDesc, !IO),
|
|
(
|
|
Success = yes,
|
|
Res = ok(detached_thread(ThreadDesc))
|
|
;
|
|
Success = no,
|
|
Res = error("Unable to spawn threads in this grade.")
|
|
).
|
|
|
|
:- pred spawn_context_2(pred(thread, io, io)::in(pred(in, di, uo) is cc_multi),
|
|
bool::out, thread_desc::out, io::di, io::uo) is cc_multi.
|
|
|
|
spawn_context_2(_, Res, "", !IO) :-
|
|
( Res = no
|
|
; Res = no
|
|
).
|
|
|
|
:- pragma foreign_proc("C",
|
|
spawn_context_2(Goal::in(pred(in, di, uo) is cc_multi), Success::out,
|
|
ThreadDesc::out, _IO0::di, _IO::uo),
|
|
[promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
#if !defined(MR_HIGHLEVEL_CODE)
|
|
{
|
|
MR_Context *ctxt;
|
|
MR_ThreadLocalMuts *tlm;
|
|
|
|
ML_incr_thread_barrier_count();
|
|
|
|
ctxt = MR_create_context(""spawn"", MR_CONTEXT_SIZE_REGULAR, NULL);
|
|
ctxt->MR_ctxt_resume = MR_ENTRY(mercury__thread__spawn_begin_thread);
|
|
|
|
tlm = MR_clone_thread_local_mutables(MR_THREAD_LOCAL_MUTABLES);
|
|
ctxt->MR_ctxt_thread_local_mutables = tlm;
|
|
|
|
// Derive a thread descriptor from the address of the thread-local mutable
|
|
// vector for the Mercury thread. It should actually be more unique than a
|
|
// context address as contexts are kept around and reused.
|
|
ThreadDesc = MR_make_string(MR_ALLOC_ID, ""%p"", tlm);
|
|
|
|
// Store Goal and ThreadDesc on the top of the new context's stack.
|
|
ctxt->MR_ctxt_sp += 2;
|
|
ctxt->MR_ctxt_sp[0] = Goal; // MR_stackvar(1)
|
|
ctxt->MR_ctxt_sp[-1] = (MR_Word) ThreadDesc; // MR_stackvar(2)
|
|
|
|
MR_schedule_context(ctxt);
|
|
|
|
Success = MR_TRUE;
|
|
}
|
|
#else // MR_HIGHLEVEL_CODE
|
|
{
|
|
Success = MR_FALSE;
|
|
ThreadDesc = MR_make_string_const("""");
|
|
}
|
|
#endif // MR_HIGHLEVEL_CODE
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
spawn_context_2(Goal::in(pred(in, di, uo) is cc_multi), Success::out,
|
|
ThreadDesc::out, _IO0::di, _IO::uo),
|
|
[promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
RunGoalDetached rg = new RunGoalDetached((Object[]) Goal);
|
|
Task task = new Task(rg);
|
|
ThreadDesc = String.valueOf(task.getId());
|
|
rg.setThreadDesc(ThreadDesc);
|
|
JavaInternal.getThreadPool().submit(task);
|
|
Success = bool.YES;
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
init_thread_options = thread_options(0u).
|
|
|
|
set_min_stack_size(MinStackSize, !Options) :-
|
|
!Options ^ min_stack_size := MinStackSize.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
spawn_native(Goal, Res, !IO) :-
|
|
spawn_native(Goal, init_thread_options, Res, !IO).
|
|
|
|
spawn_native(Goal, Options, Res, !IO) :-
|
|
Options = thread_options(MinStackSize),
|
|
Dummy = 0, % for the typeinfo
|
|
spawn_native_2(Goal, Dummy, MinStackSize, Success, ThreadDesc, ErrorMsg,
|
|
!IO),
|
|
(
|
|
Success = yes,
|
|
Res = ok(detached_thread(ThreadDesc))
|
|
;
|
|
Success = no,
|
|
Res = error(ErrorMsg)
|
|
).
|
|
|
|
:- pred spawn_native_2(pred(thread, io, io)::in(pred(in, di, uo) is cc_multi),
|
|
T::in, uint::in, bool::out, thread_desc::out, string::out,
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
:- pragma foreign_proc("C",
|
|
spawn_native_2(Goal::in(pred(in, di, uo) is cc_multi), _T::in,
|
|
MinStackSize::in, Success::out, ThreadDesc::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
#ifdef MR_THREAD_SAFE
|
|
pthread_t thread_handle;
|
|
|
|
// Pass 0 for joinable_thread_mutvar to create a detached thread.
|
|
Success = ML_create_exclusive_thread(TypeInfo_for_T, Goal,
|
|
MinStackSize, (MR_Word) 0, &ThreadDesc, &thread_handle, &ErrorMsg,
|
|
MR_ALLOC_ID);
|
|
#else
|
|
Success = MR_FALSE;
|
|
ThreadDesc = MR_make_string_const("""");
|
|
ErrorMsg = MR_make_string_const(
|
|
""Cannot create native thread in this grade."");
|
|
#endif
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
spawn_native_2(Goal::in(pred(in, di, uo) is cc_multi), _T::in,
|
|
_MinStackSize::in, Success::out, ThreadDesc::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
try {
|
|
object[] thread_locals = runtime.ThreadLocalMutables.clone();
|
|
RunGoalDetached rg = new RunGoalDetached(Goal, thread_locals);
|
|
System.Threading.Thread thread = new System.Threading.Thread(
|
|
new System.Threading.ThreadStart(rg.run));
|
|
string thread_desc = thread.ManagedThreadId.ToString();
|
|
rg.setThreadDesc(thread_desc);
|
|
thread.Start();
|
|
|
|
Success = mr_bool.YES;
|
|
ThreadDesc = thread_desc;
|
|
ErrorMsg = """";
|
|
} catch (System.SystemException e) {
|
|
// This includes System.Threading.ThreadStartException.
|
|
// SystemException has been seen with mono.
|
|
Success = mr_bool.NO;
|
|
ThreadDesc = """";
|
|
ErrorMsg = e.Message;
|
|
}
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
spawn_native_2(Goal::in(pred(in, di, uo) is cc_multi), _T::in,
|
|
_MinStackSize::in, Success::out, ThreadDesc::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
try {
|
|
RunGoalDetached rg = new RunGoalDetached((Object[]) Goal);
|
|
Task task = new Task(rg);
|
|
String thread_desc = String.valueOf(task.getId());
|
|
rg.setThreadDesc(thread_desc);
|
|
JavaInternal.getThreadPool().createExclusiveThread(task).start();
|
|
|
|
Success = bool.YES;
|
|
ThreadDesc = thread_desc;
|
|
ErrorMsg = """";
|
|
} catch (java.lang.SecurityException e) {
|
|
Success = bool.NO;
|
|
ThreadDesc = """";
|
|
ErrorMsg = e.getMessage();
|
|
} catch (java.lang.OutOfMemoryError e) {
|
|
Success = bool.NO;
|
|
ThreadDesc = """";
|
|
ErrorMsg = e.getMessage();
|
|
}
|
|
if (Success == bool.NO && ErrorMsg == null) {
|
|
ErrorMsg = ""Unable to create new native thread."";
|
|
}
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
spawn_native_joinable(Goal, Options, Res, !IO) :-
|
|
Options = thread_options(MinStackSize),
|
|
promise_pure (
|
|
impure new_mutvar0(OutputMutvar),
|
|
spawn_native_joinable_2(Goal, MinStackSize, OutputMutvar,
|
|
Success, ThreadHandle, ErrorMsg, !IO)
|
|
),
|
|
(
|
|
Success = yes,
|
|
Res = ok(joinable_thread(ThreadHandle, OutputMutvar))
|
|
;
|
|
Success = no,
|
|
Res = error(ErrorMsg)
|
|
).
|
|
|
|
:- pred spawn_native_joinable_2(
|
|
pred(joinable_thread(T), T, io, io)::in(pred(in, out, di, uo) is cc_multi),
|
|
uint::in, mutvar(T)::in, bool::out, thread_handle::out, string::out,
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
:- pragma foreign_proc("C",
|
|
spawn_native_joinable_2(Goal::in(pred(in, out, di, uo) is cc_multi),
|
|
MinStackSize::in, OutputMutvar::in,
|
|
Success::out, ThreadHandle::out, ErrorMsg::out, _IO0::di, _IO::uo),
|
|
[promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
#ifdef MR_THREAD_SAFE
|
|
MR_String thread_desc;
|
|
|
|
Success = ML_create_exclusive_thread(TypeInfo_for_T, Goal,
|
|
MinStackSize, OutputMutvar, &thread_desc, &ThreadHandle, &ErrorMsg,
|
|
MR_ALLOC_ID);
|
|
#else
|
|
Success = MR_FALSE;
|
|
ThreadHandle = 0;
|
|
ErrorMsg = MR_make_string_const(
|
|
""Cannot create joinable thread in this grade."");
|
|
#endif
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
spawn_native_joinable_2(Goal::in(pred(in, out, di, uo) is cc_multi),
|
|
_MinStackSize::in, OutputMutvar::in,
|
|
Success::out, ThreadHandle::out, ErrorMsg::out, _IO0::di, _IO::uo),
|
|
[promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
try {
|
|
object[] thread_locals = runtime.ThreadLocalMutables.clone();
|
|
RunGoalJoinable rg = new RunGoalJoinable(TypeInfo_for_T, Goal,
|
|
thread_locals, OutputMutvar);
|
|
System.Threading.Thread thread = new System.Threading.Thread(
|
|
new System.Threading.ThreadStart(rg.run));
|
|
rg.setThreadHandle(thread);
|
|
thread.Start();
|
|
|
|
Success = mr_bool.YES;
|
|
ThreadHandle = thread;
|
|
ErrorMsg = """";
|
|
} catch (System.SystemException e) {
|
|
// This includes System.Threading.ThreadStartException.
|
|
// SystemException has been seen with mono.
|
|
Success = mr_bool.NO;
|
|
ThreadHandle = null;
|
|
ErrorMsg = e.Message;
|
|
}
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
spawn_native_joinable_2(Goal::in(pred(in, out, di, uo) is cc_multi),
|
|
_MinStackSize::in, OutputMutvar::in,
|
|
Success::out, ThreadHandle::out, ErrorMsg::out, _IO0::di, _IO::uo),
|
|
[promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
try {
|
|
RunGoalJoinable rg = new RunGoalJoinable(TypeInfo_for_T,
|
|
(Object[]) Goal, OutputMutvar);
|
|
Task task = new Task(rg);
|
|
MercuryThread mt = JavaInternal.getThreadPool()
|
|
.createExclusiveThread(task);
|
|
rg.setThreadHandle(mt);
|
|
mt.start();
|
|
|
|
Success = bool.YES;
|
|
ThreadHandle = mt;
|
|
ErrorMsg = """";
|
|
} catch (java.lang.SecurityException e) {
|
|
Success = bool.NO;
|
|
ThreadHandle = null;
|
|
ErrorMsg = e.getMessage();
|
|
} catch (java.lang.OutOfMemoryError e) {
|
|
Success = bool.NO;
|
|
ThreadHandle = null;
|
|
ErrorMsg = e.getMessage();
|
|
}
|
|
if (Success == bool.NO && ErrorMsg == null) {
|
|
ErrorMsg = ""Unable to create new native thread."";
|
|
}
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
join_thread(Thread, Res, !IO) :-
|
|
Thread = joinable_thread(ThreadHandle, OutputMutvar),
|
|
promise_pure (
|
|
join_thread_2(ThreadHandle, Success, ErrorMsg, !IO),
|
|
(
|
|
Success = yes,
|
|
impure get_mutvar(OutputMutvar, Output),
|
|
Res0 = ok(Output)
|
|
;
|
|
Success = no,
|
|
Res0 = error(ErrorMsg)
|
|
)
|
|
),
|
|
cc_multi_equal(Res0, Res).
|
|
|
|
:- pred join_thread_2(thread_handle::in, bool::out, string::out,
|
|
io::di, io::uo) is det.
|
|
|
|
:- pragma foreign_proc("C",
|
|
join_thread_2(ThreadHandle::in, Success::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
#ifdef MR_THREAD_SAFE
|
|
int err;
|
|
char errbuf[MR_STRERROR_BUF_SIZE];
|
|
|
|
err = pthread_join(ThreadHandle, NULL);
|
|
if (err == 0) {
|
|
Success = MR_YES;
|
|
ErrorMsg = MR_make_string_const("""");
|
|
} else {
|
|
Success = MR_NO;
|
|
ErrorMsg = MR_make_string(MR_ALLOC_ID, ""pthread_join failed: %s"",
|
|
MR_strerror(err, errbuf, sizeof(errbuf)));
|
|
}
|
|
#else
|
|
Success = MR_NO;
|
|
ErrorMsg = MR_make_string_const(
|
|
""Native threads are not supported in this grade."");
|
|
#endif
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
join_thread_2(ThreadHandle::in, Success::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
try {
|
|
ThreadHandle.Join();
|
|
Success = mr_bool.YES;
|
|
ErrorMsg = """";
|
|
} catch (System.SystemException e) {
|
|
Success = mr_bool.NO;
|
|
ErrorMsg = e.Message;
|
|
}
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
join_thread_2(ThreadHandle::in, Success::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
try {
|
|
ThreadHandle.join();
|
|
Success = bool.YES;
|
|
ErrorMsg = """";
|
|
} catch (java.lang.InterruptedException e) {
|
|
Success = bool.NO;
|
|
ErrorMsg = e.getMessage();
|
|
}
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma no_inline(pred(yield/2)).
|
|
:- pragma foreign_proc("C",
|
|
yield(_IO0::di, _IO::uo),
|
|
[promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
MR_save_context(MR_ENGINE(MR_eng_this_context));
|
|
#ifdef ML_THREAD_AVOID_LABEL_ADDRS
|
|
MR_ENGINE(MR_eng_this_context)->MR_ctxt_resume =
|
|
MR_ENTRY(mercury__thread__yield_resume);
|
|
#else
|
|
MR_ENGINE(MR_eng_this_context)->MR_ctxt_resume =
|
|
&&yield_skip_to_the_end;
|
|
#endif
|
|
MR_schedule_context(MR_ENGINE(MR_eng_this_context));
|
|
MR_ENGINE(MR_eng_this_context) = NULL;
|
|
MR_idle();
|
|
|
|
#ifndef ML_THREAD_AVOID_LABEL_ADDRS
|
|
yield_skip_to_the_end:
|
|
#endif
|
|
#endif
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
yield(_IO0::di, _IO::uo),
|
|
[promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
System.Threading.Thread.Yield();
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
yield(_IO0::di, _IO::uo),
|
|
[promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
java.lang.Thread.yield();
|
|
").
|
|
|
|
yield(!IO).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Low-level C implementation.
|
|
%
|
|
|
|
:- pragma foreign_decl("C",
|
|
"
|
|
/*
|
|
INIT mercury_sys_init_thread_modules
|
|
*/
|
|
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
MR_define_extern_entry(mercury__thread__spawn_begin_thread);
|
|
MR_declare_label(mercury__thread__spawn_end_thread);
|
|
MR_define_extern_entry(mercury__thread__yield_resume);
|
|
#endif
|
|
").
|
|
|
|
:- pragma foreign_code("C",
|
|
"
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
|
|
MR_declare_entry(mercury__do_call_closure_1);
|
|
|
|
MR_BEGIN_MODULE(hand_written_thread_module)
|
|
MR_init_entry_ai(mercury__thread__spawn_begin_thread);
|
|
MR_init_label(mercury__thread__spawn_end_thread);
|
|
MR_init_entry_ai(mercury__thread__yield_resume);
|
|
MR_BEGIN_CODE
|
|
|
|
MR_define_entry(mercury__thread__spawn_begin_thread);
|
|
{
|
|
// Call the closure placed the top of the stack.
|
|
MR_r1 = MR_stackvar(1); // Goal
|
|
MR_r2 = MR_stackvar(2); // ThreadDesc
|
|
MR_decr_sp(2);
|
|
MR_noprof_call(MR_ENTRY(mercury__do_call_closure_1),
|
|
MR_LABEL(mercury__thread__spawn_end_thread));
|
|
}
|
|
MR_define_label(mercury__thread__spawn_end_thread);
|
|
{
|
|
ML_decr_thread_barrier_count();
|
|
|
|
MR_save_context(MR_ENGINE(MR_eng_this_context));
|
|
MR_release_context(MR_ENGINE(MR_eng_this_context));
|
|
MR_ENGINE(MR_eng_this_context) = NULL;
|
|
MR_idle();
|
|
}
|
|
|
|
MR_define_entry(mercury__thread__yield_resume);
|
|
{
|
|
MR_proceed();
|
|
}
|
|
MR_END_MODULE
|
|
|
|
#endif
|
|
|
|
// Forward decls to suppress gcc warnings.
|
|
void mercury_sys_init_thread_modules_init(void);
|
|
void mercury_sys_init_thread_modules_init_type_tables(void);
|
|
#ifdef MR_DEEP_PROFILING
|
|
void mercury_sys_init_thread_modules_write_out_proc_statics(
|
|
FILE *deep_fp, FILE *procrep_fp);
|
|
#endif
|
|
|
|
void mercury_sys_init_thread_modules_init(void)
|
|
{
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
hand_written_thread_module();
|
|
#endif
|
|
}
|
|
|
|
void mercury_sys_init_thread_modules_init_type_tables(void)
|
|
{
|
|
// No types to register.
|
|
}
|
|
|
|
#ifdef MR_DEEP_PROFILING
|
|
void mercury_sys_init_thread_modules_write_out_proc_statics(FILE *deep_fp,
|
|
FILE *procrep_fp)
|
|
{
|
|
// No proc_statics to write out.
|
|
}
|
|
#endif
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% High-level C and low-level C exclusive threads.
|
|
%
|
|
|
|
:- pragma foreign_decl("C", local, "
|
|
#if defined(MR_THREAD_SAFE)
|
|
|
|
#include <pthread.h>
|
|
|
|
static MR_bool ML_create_exclusive_thread(MR_Word typeinfo_for_T, MR_Word goal,
|
|
size_t min_stack_size, MR_Word joinable_thread_mutvar,
|
|
MR_String *thread_desc, pthread_t *thread_handle,
|
|
MR_String *error_msg, MR_AllocSiteInfoPtr alloc_id);
|
|
static void *ML_exclusive_thread_wrapper(void *arg);
|
|
|
|
typedef struct ML_ThreadWrapperArgs ML_ThreadWrapperArgs;
|
|
struct ML_ThreadWrapperArgs {
|
|
MercuryLock mutex;
|
|
MercuryCond cond;
|
|
MR_Word typeinfo_for_T;
|
|
MR_Word goal;
|
|
MR_ThreadLocalMuts *thread_local_mutables;
|
|
MR_Word joinable_thread_mutvar; // 0 for detached thread
|
|
MR_Integer thread_state;
|
|
MR_String thread_desc;
|
|
};
|
|
|
|
enum {
|
|
ML_THREAD_NOT_READY,
|
|
ML_THREAD_READY,
|
|
ML_THREAD_START_ERROR
|
|
};
|
|
|
|
#endif // MR_THREAD_SAFE
|
|
").
|
|
|
|
:- pragma foreign_code("C", "
|
|
#if defined(MR_THREAD_SAFE)
|
|
static MR_bool
|
|
ML_create_exclusive_thread(MR_Word typeinfo_for_T, MR_Word goal,
|
|
size_t min_stack_size, MR_Word joinable_thread_mutvar,
|
|
MR_String *thread_desc, pthread_t *thread_handle,
|
|
MR_String *error_msg, MR_AllocSiteInfoPtr alloc_id)
|
|
{
|
|
ML_ThreadWrapperArgs args;
|
|
pthread_t thread;
|
|
pthread_attr_t attrs;
|
|
int err;
|
|
char errbuf[MR_STRERROR_BUF_SIZE];
|
|
|
|
*thread_desc = MR_make_string_const("""");
|
|
*thread_handle = MR_null_thread();
|
|
*error_msg = MR_make_string_const("""");
|
|
|
|
ML_incr_thread_barrier_count();
|
|
|
|
// The obvious synchronisation object to use here is a semaphore,
|
|
// but glibc < 2.21 had a bug which could result in sem_post reading
|
|
// from a semaphore after (in another thread) sem_wait returns and
|
|
// destroys the semaphore.
|
|
// <https://sourceware.org/bugzilla/show_bug.cgi?id=12674>
|
|
|
|
pthread_mutex_init(&args.mutex, MR_MUTEX_ATTR);
|
|
pthread_cond_init(&args.cond, MR_COND_ATTR);
|
|
args.typeinfo_for_T = typeinfo_for_T;
|
|
args.goal = goal;
|
|
args.thread_local_mutables =
|
|
MR_clone_thread_local_mutables(MR_THREAD_LOCAL_MUTABLES);
|
|
args.joinable_thread_mutvar = joinable_thread_mutvar;
|
|
// These fields will be updated by the newly created thread.
|
|
args.thread_state = ML_THREAD_NOT_READY;
|
|
args.thread_desc = NULL;
|
|
|
|
pthread_attr_init(&attrs);
|
|
if (joinable_thread_mutvar == 0) {
|
|
err = pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
|
|
if (err != 0) {
|
|
*error_msg = MR_make_string(alloc_id,
|
|
""pthread_attr_setdetachstate failed: %s"",
|
|
MR_strerror(errno, errbuf, sizeof(errbuf)));
|
|
goto failed_to_create_thread;
|
|
}
|
|
}
|
|
if (min_stack_size > 0) {
|
|
err = pthread_attr_setstacksize(&attrs, min_stack_size);
|
|
if (err != 0) {
|
|
*error_msg = MR_make_string(alloc_id,
|
|
""pthread_attr_setstacksize failed: %s"",
|
|
MR_strerror(errno, errbuf, sizeof(errbuf)));
|
|
goto failed_to_create_thread;
|
|
}
|
|
}
|
|
|
|
err = pthread_create(&thread, &attrs, ML_exclusive_thread_wrapper, &args);
|
|
if (err != 0) {
|
|
*error_msg = MR_make_string(alloc_id, ""pthread_create failed: %s"",
|
|
MR_strerror(errno, errbuf, sizeof(errbuf)));
|
|
goto failed_to_create_thread;
|
|
}
|
|
|
|
MR_LOCK(&args.mutex, ""ML_create_exclusive_thread"");
|
|
while (args.thread_state == ML_THREAD_NOT_READY) {
|
|
err = MR_COND_WAIT(&args.cond, &args.mutex,
|
|
""ML_create_exclusive_thread"");
|
|
// EINTR should not be possible, but it has happened before.
|
|
if (err != 0 && errno != EINTR) {
|
|
MR_fatal_error(
|
|
""ML_create_exclusive_thread: MR_COND_WAIT error: %s"",
|
|
MR_strerror(errno, errbuf, sizeof(errbuf)));
|
|
}
|
|
}
|
|
MR_UNLOCK(&args.mutex, ""ML_create_exclusive_thread"");
|
|
|
|
if (args.thread_state == ML_THREAD_START_ERROR) {
|
|
*error_msg =
|
|
MR_make_string_const(""Error setting up engine for thread."");
|
|
}
|
|
|
|
failed_to_create_thread:
|
|
pthread_attr_destroy(&attrs);
|
|
pthread_cond_destroy(&args.cond);
|
|
pthread_mutex_destroy(&args.mutex);
|
|
|
|
if (args.thread_state == ML_THREAD_READY) {
|
|
*thread_desc = args.thread_desc;
|
|
*thread_handle = thread;
|
|
return MR_TRUE;
|
|
}
|
|
|
|
ML_decr_thread_barrier_count();
|
|
return MR_FALSE;
|
|
}
|
|
|
|
static void *ML_exclusive_thread_wrapper(void *arg)
|
|
{
|
|
ML_ThreadWrapperArgs *args = arg;
|
|
MR_Word typeinfo_for_T;
|
|
MR_Word goal;
|
|
MR_Word joinable_thread_mutvar;
|
|
MR_String thread_desc;
|
|
pthread_t thread_handle;
|
|
|
|
if (MR_init_thread(MR_use_now) == MR_FALSE) {
|
|
MR_LOCK(&args->mutex, ""ML_exclusive_thread_wrapper"");
|
|
args->thread_state = ML_THREAD_START_ERROR;
|
|
MR_COND_SIGNAL(&args->cond, ""ML_exclusive_thread_wrapper"");
|
|
MR_UNLOCK(&args->mutex, ""ML_exclusive_thread_wrapper"");
|
|
return NULL;
|
|
}
|
|
|
|
// Set the context to have the current engine as its exclusive engine.
|
|
MR_assert(MR_ENGINE(MR_eng_this_context) != NULL);
|
|
MR_ENGINE(MR_eng_this_context)->MR_ctxt_exclusive_engine =
|
|
MR_ENGINE(MR_eng_id);
|
|
|
|
MR_assert(MR_THREAD_LOCAL_MUTABLES == NULL);
|
|
MR_SET_THREAD_LOCAL_MUTABLES(args->thread_local_mutables);
|
|
|
|
// Take a copy of args fields.
|
|
typeinfo_for_T = args->typeinfo_for_T;
|
|
goal = args->goal;
|
|
joinable_thread_mutvar = args->joinable_thread_mutvar;
|
|
|
|
thread_desc = MR_make_string(MR_ALLOC_SITE_RUNTIME,
|
|
""%"" MR_INTEGER_LENGTH_MODIFIER ""x"", MR_SELF_THREAD_ID);
|
|
thread_handle = pthread_self();
|
|
|
|
// Tell the 'parent' we are ready, passing back a thread descriptor and
|
|
// thread handle.
|
|
MR_LOCK(&args->mutex, ""ML_exclusive_thread_wrapper"");
|
|
args->thread_state = ML_THREAD_READY;
|
|
args->thread_desc = thread_desc;
|
|
MR_COND_SIGNAL(&args->cond, ""ML_exclusive_thread_wrapper"");
|
|
MR_UNLOCK(&args->mutex, ""ML_exclusive_thread_wrapper"");
|
|
// We must not dereference args after this point.
|
|
|
|
if (joinable_thread_mutvar == 0) {
|
|
ML_call_back_to_mercury_detached_cc_multi(goal, thread_desc);
|
|
} else {
|
|
ML_call_back_to_mercury_joinable_cc_multi(typeinfo_for_T, goal,
|
|
thread_handle, joinable_thread_mutvar);
|
|
}
|
|
|
|
MR_finalize_thread_engine();
|
|
|
|
ML_decr_thread_barrier_count();
|
|
|
|
return NULL;
|
|
}
|
|
#endif // MR_THREAD_SAFE
|
|
").
|
|
|
|
:- pred call_back_to_mercury_detached(
|
|
pred(thread, io, io)::in(pred(in, di, uo) is cc_multi),
|
|
thread_desc::in, io::di, io::uo) is cc_multi.
|
|
:- pragma foreign_export("C",
|
|
call_back_to_mercury_detached(in(pred(in, di, uo) is cc_multi),
|
|
in, di, uo), "ML_call_back_to_mercury_detached_cc_multi").
|
|
:- pragma foreign_export("C#",
|
|
call_back_to_mercury_detached(in(pred(in, di, uo) is cc_multi),
|
|
in, di, uo), "ML_call_back_to_mercury_detached_cc_multi").
|
|
:- pragma foreign_export("Java",
|
|
call_back_to_mercury_detached(in(pred(in, di, uo) is cc_multi),
|
|
in, di, uo), "ML_call_back_to_mercury_detached_cc_multi").
|
|
:- pragma no_inline(pred(call_back_to_mercury_detached/4)).
|
|
|
|
call_back_to_mercury_detached(Goal, ThreadDesc, !IO) :-
|
|
Thread = detached_thread(ThreadDesc),
|
|
Goal(Thread, !IO).
|
|
|
|
:- pred call_back_to_mercury_joinable(
|
|
pred(joinable_thread(T), T, io, io)::in(pred(in, out, di, uo) is cc_multi),
|
|
thread_handle::in, mutvar(T)::in, io::di, io::uo) is cc_multi.
|
|
:- pragma foreign_export("C",
|
|
call_back_to_mercury_joinable(in(pred(in, out, di, uo) is cc_multi),
|
|
in, in, di, uo), "ML_call_back_to_mercury_joinable_cc_multi").
|
|
:- pragma foreign_export("C#",
|
|
call_back_to_mercury_joinable(in(pred(in, out, di, uo) is cc_multi),
|
|
in, in, di, uo), "ML_call_back_to_mercury_joinable_cc_multi").
|
|
:- pragma foreign_export("Java",
|
|
call_back_to_mercury_joinable(in(pred(in, out, di, uo) is cc_multi),
|
|
in, in, di, uo), "ML_call_back_to_mercury_joinable_cc_multi").
|
|
:- pragma no_inline(pred(call_back_to_mercury_joinable/5)).
|
|
|
|
call_back_to_mercury_joinable(Goal, ThreadHandle, OutputMutvar, !IO) :-
|
|
Thread = joinable_thread(ThreadHandle, OutputMutvar),
|
|
promise_pure (
|
|
Goal(Thread, Output, !IO),
|
|
% Store a reference to the output term in a mutvar that is in turn
|
|
% referenced from a joinable_thread term. If we simply returned the
|
|
% output term as the return value of the pthread_create start routine
|
|
% (ML_exclusive_thread_wrapper), it might be possible that the last
|
|
% reference to the term resides only in some GC-inaccessible memory
|
|
% in the pthread implementation, and therefore could be collected
|
|
% before join_thread retrieves the value.
|
|
%
|
|
% The C# or Java thread APIs do not support returning a value from a
|
|
% joined thread anyway.
|
|
impure set_mutvar(OutputMutvar, Output)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_decl("C", local,
|
|
"
|
|
#if defined(MR_THREAD_SAFE) || !defined(MR_HIGHLEVEL_CODE)
|
|
static void ML_incr_thread_barrier_count(void);
|
|
static void ML_decr_thread_barrier_count(void);
|
|
#endif
|
|
").
|
|
|
|
:- pragma foreign_code("C",
|
|
"
|
|
#if defined(MR_THREAD_SAFE) || !defined(MR_HIGHLEVEL_CODE)
|
|
|
|
static void ML_incr_thread_barrier_count(void)
|
|
{
|
|
MR_LOCK(&MR_thread_barrier_lock, ""ML_incr_thread_barrier_count"");
|
|
MR_thread_barrier_count++;
|
|
MR_UNLOCK(&MR_thread_barrier_lock, ""ML_incr_thread_barrier_count"");
|
|
}
|
|
|
|
static void ML_decr_thread_barrier_count(void)
|
|
{
|
|
MR_LOCK(&MR_thread_barrier_lock, ""ML_decr_thread_barrier_count"");
|
|
MR_thread_barrier_count--;
|
|
#ifdef MR_HIGHLEVEL_CODE
|
|
if (MR_thread_barrier_count == 0) {
|
|
MR_COND_SIGNAL(&MR_thread_barrier_cond,
|
|
""ML_decr_thread_barrier_count"");
|
|
}
|
|
#else
|
|
if (MR_thread_barrier_count == 0) {
|
|
// If this is the last spawned context to terminate and the
|
|
// main context was just waiting on us in order to terminate,
|
|
// then reschedule the main context.
|
|
|
|
if (MR_thread_barrier_context) {
|
|
MR_schedule_context(MR_thread_barrier_context);
|
|
MR_thread_barrier_context = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
MR_UNLOCK(&MR_thread_barrier_lock, ""ML_decr_thread_barrier_count"");
|
|
}
|
|
|
|
#endif // MR_THREAD_SAFE || !MR_HIGHLEVEL_CODE
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_code("C#", "
|
|
private class RunGoalDetached {
|
|
private object[] goal;
|
|
private object[] thread_local_mutables;
|
|
private string thread_desc;
|
|
|
|
internal RunGoalDetached(object[] goal, object[] tlmuts)
|
|
{
|
|
this.goal = goal;
|
|
this.thread_local_mutables = tlmuts;
|
|
this.thread_desc = null;
|
|
}
|
|
|
|
internal void setThreadDesc(string thread_desc)
|
|
{
|
|
this.thread_desc = thread_desc;
|
|
}
|
|
|
|
internal void run()
|
|
{
|
|
runtime.ThreadLocalMutables.set_array(thread_local_mutables);
|
|
thread.ML_call_back_to_mercury_detached_cc_multi(goal, thread_desc);
|
|
}
|
|
}
|
|
|
|
private class RunGoalJoinable {
|
|
private runtime.TypeInfo_Struct typeinfo_for_T;
|
|
private object[] goal;
|
|
private object[] thread_local_mutables;
|
|
private object[] output_mutvar;
|
|
private System.Threading.Thread thread_handle;
|
|
|
|
internal RunGoalJoinable(runtime.TypeInfo_Struct typeinfo_for_T,
|
|
object[] goal, object[] tlmuts, object[] output_mutvar)
|
|
{
|
|
this.typeinfo_for_T = typeinfo_for_T;
|
|
this.goal = goal;
|
|
this.thread_local_mutables = tlmuts;
|
|
this.output_mutvar = output_mutvar;
|
|
this.thread_handle = null;
|
|
}
|
|
|
|
internal void setThreadHandle(System.Threading.Thread thread_handle)
|
|
{
|
|
this.thread_handle = thread_handle;
|
|
}
|
|
|
|
internal void run()
|
|
{
|
|
runtime.ThreadLocalMutables.set_array(thread_local_mutables);
|
|
thread.ML_call_back_to_mercury_joinable_cc_multi(typeinfo_for_T, goal,
|
|
thread_handle, output_mutvar);
|
|
}
|
|
}
|
|
").
|
|
|
|
:- pragma foreign_code("Java", "
|
|
public static class RunGoalDetached implements Runnable {
|
|
private final Object[] goal;
|
|
private String thread_desc;
|
|
|
|
private RunGoalDetached(Object[] goal)
|
|
{
|
|
this.goal = goal;
|
|
this.thread_desc = null;
|
|
}
|
|
|
|
private void setThreadDesc(String thread_desc)
|
|
{
|
|
this.thread_desc = thread_desc;
|
|
}
|
|
|
|
public void run()
|
|
{
|
|
thread.ML_call_back_to_mercury_detached_cc_multi(goal, thread_desc);
|
|
}
|
|
}
|
|
|
|
public static class RunGoalJoinable implements Runnable {
|
|
private final jmercury.runtime.TypeInfo_Struct typeinfo_for_T;
|
|
private final Object[] goal;
|
|
private final mutvar.Mutvar output_mutvar;
|
|
private MercuryThread thread_handle;
|
|
|
|
private RunGoalJoinable(jmercury.runtime.TypeInfo_Struct typeinfo_for_T,
|
|
Object[] goal, mutvar.Mutvar output_mutvar)
|
|
{
|
|
this.typeinfo_for_T = typeinfo_for_T;
|
|
this.goal = goal;
|
|
this.output_mutvar = output_mutvar;
|
|
this.thread_handle = null;
|
|
}
|
|
|
|
private void setThreadHandle(MercuryThread thread_handle)
|
|
{
|
|
this.thread_handle = thread_handle;
|
|
}
|
|
|
|
public void run()
|
|
{
|
|
thread.ML_call_back_to_mercury_joinable_cc_multi(typeinfo_for_T, goal,
|
|
thread_handle, output_mutvar);
|
|
}
|
|
}
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
num_processors(MaybeProcs, !IO) :-
|
|
num_processors(Procs, Success, !IO),
|
|
(
|
|
Success = yes,
|
|
MaybeProcs = yes(Procs)
|
|
;
|
|
Success = no,
|
|
MaybeProcs = no
|
|
).
|
|
|
|
:- pred num_processors(int::out, bool::out, io::di, io::uo) is det.
|
|
|
|
:- pragma foreign_proc("C",
|
|
num_processors(Procs::out, Success::out, _IO0::di, _IO::uo),
|
|
[promise_pure, thread_safe, will_not_call_mercury,
|
|
will_not_throw_exception, tabled_for_io],
|
|
"
|
|
#ifdef MR_THREAD_SAFE
|
|
// MR_get_num_processors() takes the global lock.
|
|
Procs = MR_get_num_processors();
|
|
#else
|
|
Procs = 0;
|
|
#endif
|
|
Success = (Procs > 0) ? MR_YES : MR_NO;
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
num_processors(Procs::out, Success::out, _IO0::di, _IO::uo),
|
|
[promise_pure, thread_safe, will_not_call_mercury,
|
|
will_not_throw_exception, tabled_for_io],
|
|
"
|
|
Procs = Runtime.getRuntime().availableProcessors();
|
|
Success = bool.YES;
|
|
").
|
|
|
|
% On other backends se don't know how to determine this yet.
|
|
num_processors(0, no, !IO).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module thread.
|
|
%---------------------------------------------------------------------------%
|