mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-30 16:54:41 +00:00
Branches: main, 11.07 (maybe)
Avoid warnings from MSVC concerning assignments from uninitialized variables.
These assignments arise due to the assignment of the initial I/O state to the
final I/O state in foreign_proc bodies. In low-level C grades this leads to
code like the following:
{
MR_Word IO0;
MR_Word IO;
...
IO = IO0;
}
Even though IO is itself unused, MSVC still emits a warning about the
assignment (many hundreds of warnings when the io module is opt-imported).
To avoid this, use don't-care variables for the I/O state in foreign procs
and don't include references to the I/O state in their bodies at all.
(We already did this in some places but not others.)
library/benchmarking.m:
library/dir.m:
library/io.m:
library/par_builtin.m:
library/stm_builtin.m:
library/thread.semaphore.m:
library/time.m:
compiler/make.util.m:
compiler/process_util.m:
Make the above change.
Delete the MR_update_io macro that is defined in a couple of spots.
library/io.m:
Unrelated change: s/MR_CONSERVATIVE_GC/MR_BOEHM_GC/ in a spot
since the former does not imply the later and the code that is
protecting is Boehm GC specific.
tests/debugger/poly_io_retry2.m:
Conform the the above change.
453 lines
13 KiB
Mathematica
453 lines
13 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2000-2001,2003-2004, 2006-2008, 2010-2011 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU Library General
|
|
% Public License - see the file COPYING.LIB in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% File: thread.m.
|
|
% Main author: conway.
|
|
% Stability: medium.
|
|
%
|
|
% This module defines the Mercury concurrency interface.
|
|
%
|
|
% The term `concurrency' here refers to threads, not necessarily to parallel
|
|
% execution. (The latter is also possible if you are using one of the .par
|
|
% grades and the lowlevel C backend, e.g. grade asm_fast.par.gc).
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module thread.
|
|
:- interface.
|
|
|
|
:- import_module io.
|
|
|
|
:- include_module channel.
|
|
:- include_module mvar.
|
|
:- include_module semaphore.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% can_spawn succeeds if spawn/3 is supported in the current grade.
|
|
%
|
|
:- pred can_spawn 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.
|
|
%
|
|
:- pred spawn(pred(io, io)::in(pred(di, uo) is cc_multi),
|
|
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.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- 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 :- external procedures so that the
|
|
** transforms won't be applied
|
|
*/
|
|
#define ML_THREAD_AVOID_LABEL_ADDRS
|
|
#endif
|
|
#endif
|
|
").
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_proc("C",
|
|
can_spawn,
|
|
[will_not_call_mercury, promise_pure, may_not_duplicate],
|
|
"
|
|
#if !defined(MR_HIGHLEVEL_CODE)
|
|
SUCCESS_INDICATOR = MR_TRUE;
|
|
#else
|
|
#if defined(MR_THREAD_SAFE)
|
|
SUCCESS_INDICATOR = MR_TRUE;
|
|
#else
|
|
SUCCESS_INDICATOR = MR_FALSE;
|
|
#endif
|
|
#endif
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
can_spawn,
|
|
[will_not_call_mercury, promise_pure],
|
|
"
|
|
SUCCESS_INDICATOR = true;
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
can_spawn,
|
|
[will_not_call_mercury, promise_pure],
|
|
"
|
|
SUCCESS_INDICATOR = true;
|
|
").
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_proc("C",
|
|
spawn(Goal::(pred(di, uo) is cc_multi), _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_LOCK(&MR_thread_barrier_lock, ""thread.spawn"");
|
|
MR_thread_barrier_count++;
|
|
MR_UNLOCK(&MR_thread_barrier_lock, ""thread.spawn"");
|
|
|
|
ctxt = MR_create_context(""spawn"", MR_CONTEXT_SIZE_REGULAR, NULL);
|
|
ctxt->MR_ctxt_resume = MR_ENTRY(mercury__thread__spawn_begin_thread);
|
|
|
|
/*
|
|
** Store the closure on the top of the new context's stack.
|
|
*/
|
|
|
|
*(ctxt->MR_ctxt_sp) = Goal;
|
|
ctxt->MR_ctxt_next = NULL;
|
|
ctxt->MR_ctxt_thread_local_mutables =
|
|
MR_clone_thread_local_mutables(MR_THREAD_LOCAL_MUTABLES);
|
|
MR_schedule_context(ctxt);
|
|
|
|
#else /* MR_HIGHLEVEL_CODE */
|
|
|
|
#if defined(MR_THREAD_SAFE)
|
|
ML_create_thread(Goal);
|
|
#else
|
|
MR_fatal_error(""spawn/3 requires a .par grade in high-level C grades."");
|
|
#endif
|
|
|
|
#endif /* MR_HIGHLEVEL_CODE */
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
spawn(Goal::(pred(di, uo) is cc_multi), _IO0::di, _IO::uo),
|
|
[promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
object[] thread_locals = runtime.ThreadLocalMutables.clone();
|
|
MercuryThread mt = new MercuryThread(Goal, thread_locals);
|
|
System.Threading.Thread thread = new System.Threading.Thread(
|
|
new System.Threading.ThreadStart(mt.execute_goal));
|
|
thread.Start();
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
spawn(Goal::(pred(di, uo) is cc_multi), IO0::di, IO::uo),
|
|
[promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
MercuryThread mt = new MercuryThread((Object[]) Goal);
|
|
Thread thread = new Thread(mt);
|
|
thread.start();
|
|
IO = IO0;
|
|
").
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pragma no_inline(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],
|
|
"
|
|
// Only available in .NET 4.0.
|
|
// System.Threading.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();
|
|
IO = IO0;
|
|
").
|
|
|
|
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_sp;
|
|
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);
|
|
{
|
|
MR_LOCK(&MR_thread_barrier_lock, ""thread__spawn_end_thread"");
|
|
MR_thread_barrier_count--;
|
|
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;
|
|
}
|
|
}
|
|
MR_UNLOCK(&MR_thread_barrier_lock, ""thread__spawn_end_thread"");
|
|
|
|
MR_destroy_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 implementation
|
|
%
|
|
|
|
:- pragma foreign_decl("C", "
|
|
#if defined(MR_HIGHLEVEL_CODE) && defined(MR_THREAD_SAFE)
|
|
#include <pthread.h>
|
|
|
|
int ML_create_thread(MR_Word goal);
|
|
void *ML_thread_wrapper(void *arg);
|
|
|
|
typedef struct ML_ThreadWrapperArgs ML_ThreadWrapperArgs;
|
|
struct ML_ThreadWrapperArgs {
|
|
MR_Word goal;
|
|
MR_ThreadLocalMuts *thread_local_mutables;
|
|
};
|
|
#endif /* MR_HIGHLEVEL_CODE && MR_THREAD_SAFE */
|
|
").
|
|
|
|
:- pragma foreign_code("C", "
|
|
#if defined(MR_HIGHLEVEL_CODE) && defined(MR_THREAD_SAFE)
|
|
int ML_create_thread(MR_Word goal)
|
|
{
|
|
ML_ThreadWrapperArgs *args;
|
|
pthread_t thread;
|
|
pthread_attr_t attrs;
|
|
|
|
/*
|
|
** We can't allocate `args' on the stack because this function may return
|
|
** before the child thread has got all the information it needs out of the
|
|
** structure.
|
|
*/
|
|
args = MR_GC_NEW_UNCOLLECTABLE(ML_ThreadWrapperArgs);
|
|
args->goal = goal;
|
|
args->thread_local_mutables =
|
|
MR_clone_thread_local_mutables(MR_THREAD_LOCAL_MUTABLES);
|
|
|
|
MR_LOCK(&MR_thread_barrier_lock, ""thread.spawn"");
|
|
MR_thread_barrier_count++;
|
|
MR_UNLOCK(&MR_thread_barrier_lock, ""thread.spawn"");
|
|
|
|
pthread_attr_init(&attrs);
|
|
pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
|
|
if (pthread_create(&thread, &attrs, ML_thread_wrapper, args)) {
|
|
MR_fatal_error(""Unable to create thread."");
|
|
}
|
|
pthread_attr_destroy(&attrs);
|
|
|
|
return MR_TRUE;
|
|
}
|
|
|
|
void *ML_thread_wrapper(void *arg)
|
|
{
|
|
ML_ThreadWrapperArgs *args = arg;
|
|
MR_Word goal;
|
|
|
|
if (MR_init_thread(MR_use_now) == MR_FALSE) {
|
|
MR_fatal_error(""Unable to init thread."");
|
|
}
|
|
|
|
MR_assert(MR_THREAD_LOCAL_MUTABLES == NULL);
|
|
MR_SET_THREAD_LOCAL_MUTABLES(args->thread_local_mutables);
|
|
|
|
goal = args->goal;
|
|
MR_GC_free(args);
|
|
|
|
ML_call_back_to_mercury_cc_multi(goal);
|
|
|
|
MR_LOCK(&MR_thread_barrier_lock, ""ML_thread_wrapper"");
|
|
MR_thread_barrier_count--;
|
|
if (MR_thread_barrier_count == 0) {
|
|
MR_SIGNAL(&MR_thread_barrier_cond, ""ML_thread_wrapper"");
|
|
}
|
|
MR_UNLOCK(&MR_thread_barrier_lock, ""ML_thread_wrapper"");
|
|
|
|
return NULL;
|
|
}
|
|
#endif /* MR_HIGHLEVEL_CODE && MR_THREAD_SAFE */
|
|
").
|
|
|
|
:- pred call_back_to_mercury(pred(io, io), io, io).
|
|
:- mode call_back_to_mercury(pred(di, uo) is cc_multi, di, uo) is cc_multi.
|
|
:- pragma foreign_export("C",
|
|
call_back_to_mercury(pred(di, uo) is cc_multi, di, uo),
|
|
"ML_call_back_to_mercury_cc_multi").
|
|
:- pragma foreign_export("IL",
|
|
call_back_to_mercury(pred(di, uo) is cc_multi, di, uo),
|
|
"ML_call_back_to_mercury_cc_multi").
|
|
:- pragma foreign_export("C#",
|
|
call_back_to_mercury(pred(di, uo) is cc_multi, di, uo),
|
|
"ML_call_back_to_mercury_cc_multi").
|
|
:- pragma foreign_export("Java",
|
|
call_back_to_mercury(pred(di, uo) is cc_multi, di, uo),
|
|
"ML_call_back_to_mercury_cc_multi").
|
|
|
|
call_back_to_mercury(Goal, !IO) :-
|
|
Goal(!IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_code("C#", "
|
|
public class MercuryThread {
|
|
object[] Goal;
|
|
object[] thread_local_mutables;
|
|
|
|
public MercuryThread(object[] g, object[] tlmuts)
|
|
{
|
|
Goal = g;
|
|
thread_local_mutables = tlmuts;
|
|
}
|
|
|
|
public void execute_goal()
|
|
{
|
|
runtime.ThreadLocalMutables.set_array(thread_local_mutables);
|
|
thread.ML_call_back_to_mercury_cc_multi(Goal);
|
|
}
|
|
}").
|
|
|
|
:- pragma foreign_code("Java", "
|
|
public static class MercuryThread implements Runnable {
|
|
final Object[] Goal;
|
|
|
|
public MercuryThread(Object[] g)
|
|
{
|
|
Goal = g;
|
|
}
|
|
|
|
public void run()
|
|
{
|
|
thread.ML_call_back_to_mercury_cc_multi(Goal);
|
|
}
|
|
}").
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module thread.
|
|
%-----------------------------------------------------------------------------%
|