Files
mercury/library/thread.semaphore.m
Zoltan Somogyi 95ff02b1bf Add options to check the ordering of module contents.
One option, --warn-non-contiguous-decls, generates warnings if the
mode declarations of a predicate or function aren't in a contiguous block
immediately following the pred or func declaration. Since this is a rare
kind of "style error", this option is enabled by default.

Two options, --warn-inconsistent-pred-order-clauses and
--warn-inconsistent-pred-order-foreign-procs, warn about inconsistencies
between (a) the order in which predicates (and functions) are declared,
and (b) the order in which they are defined. The two options differ in
their scope. The latter applies to all predicates and functions defined
in the module, while the former applies only to those whose definitions
include Mercury clauses.

Since an exported predicate or function may need nonexported auxiliary
predicates and/or functions, imposing a single order the declarations and
definitions of *all* the predicates and functions in a module is not a good
idea. Instead, both options divide the predicates and functions defined
in a module two groups, the exported and the nonexported, and expect
a consistent order only within each group.

The result is output that looks like this:

    time.m:021: Warning: the order of the declarations and definitions of the
    time.m:021:   exported predicates is inconsistent, as shown by this diff:
    time.m:021:
    time.m:021:   --- declaration order
    time.m:021:   +++ definition order
    time.m:021:   @@ -1,7 +1,7 @@
    time.m:021:    predicate `clock'/3
    time.m:021:   -predicate `time'/3
    time.m:021:    predicate `times'/4
    time.m:021:    function `clk_tck'/0
    time.m:021:   +predicate `time'/3
    time.m:021:    function `difftime'/2
    time.m:021:    predicate `localtime'/4
    time.m:021:    function `localtime'/1

compiler/options.m:
doc/user_guide.texi:
    Add the new options.

compiler/style_checks.m:
    A new module that generates the new warnings if warranted.

compiler/check_hlds.m:
compiler/notes/compiler_design.html:
    Include and document the new module.

compiler/mercury_compile_front_end.m:
    Invoke the new module if any of the three new options is set.

compiler/hlds_pred.m:
    Record the item number of every predicate, function, and mode declaration
    in the module being compiled. We need this for information for the
    new warnings.

compiler/hlds_module.m:
    Record the context of the module declaration. We use this context
    for warnings about inconsistent order, since there isn't a better one.

compiler/hlds_clauses.m:
    Add a mechanism to retrieve the item numbers of a set of clauses
    even if they are contiguous.

    Document some old data types.

compiler/error_util.m:
    Add a new phase for style checks.

compiler/accumulator.m:
compiler/add_class.m:
compiler/add_mutable_aux_preds.m:
compiler/add_pragma_tabling.m:
compiler/add_pred.m:
compiler/add_solver.m:
compiler/add_special_pred.m:
compiler/check_typeclass.m:
compiler/clause_to_proc.m:
compiler/from_ground_term_util.m:
compiler/lambda.m:
compiler/make_hlds.m:
compiler/make_hlds_passes.m:
compiler/mercury_compile.m:
compiler/par_loop_control.m:
compiler/polymorphism.m:
compiler/stm_expand.m:
compiler/table_gen.m:
compiler/unify_proc.m:
    Conform the changes to the HLDS above.

compiler/typecheck_errors.m:
    Fix style of error messages.

library/array2d.m:
library/assoc_list.m:
library/benchmarking.m:
library/bit_buffer.write.m:
library/bool.m:
library/builtin.m:
library/construct.m:
library/cord.m:
library/counter.m:
library/float.m:
library/injection.m:
library/lazy.m:
library/lexer.m:
library/ops.m:
library/private_builtin.m:
library/profiling_builtin.m:
library/prolog.m:
library/queue.m:
library/rational.m:
library/require.m:
library/stack.m:
library/std_util.m:
library/store.m:
library/thread.semaphore.m:
library/tree234.m:
library/univ.m:
library/version_store.m:
    Move declarations or definitions around to avoid some of the warnings
    that we can now generate. (There are many more left.)

    Make some minor style improvements in the process.

tests/warnings/inconsistent_pred_order.{m,exp}:
tests/warnings/non_contiguous_decls.{m,exp}:
    New test cases to test the new options. They are both copies of
    tests/benchmarks/queens.m, with intentionally-screwed-up style.

tests/warnings/Mmakefile:
    Enable the new test cases.

tests/warnings/Mercury.options:
    Specify the options being tested for the new test cases.

tests/benchmarks/queens.m:
    Bring the style of this module up to date (before copying it).

tests/invalid/mode_decl_in_wrong_section.err_exp:
    Expect the warnings we now generate.
2016-10-15 17:26:32 +11:00

547 lines
15 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2000-2001,2003-2004, 2006-2007, 2009-2011 The University of Melbourne.
% Copyright (C) 2014 The Mercury Team.
% 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.semaphore.m.
% Main author: conway.
% Stability: medium.
%
% This module implements a simple semaphore data type for allowing
% threads to synchronise with one another.
%
% The operations in this module are no-ops in the hlc grades that do not
% contain a .par component.
%
%---------------------------------------------------------------------------%
:- module thread.semaphore.
:- interface.
:- import_module bool.
:- import_module io.
%---------------------------------------------------------------------------%
:- type semaphore.
% init(Count, Sem, !IO) creates a new semaphore `Sem' with its counter
% initialized to `Count'.
%
:- pred init(int::in, semaphore::uo, io::di, io::uo) is det.
% init(Sem, !IO) creates a new semaphore `Sem' with its counter
% initialized to 0.
%
:- pred init(semaphore::uo, io::di, io::uo) is det.
% Sem = init(Count) returns a new semaphore `Sem' with its counter
% initialized to `Count'.
%
:- impure func impure_init(int::in) = (semaphore::uo) is det.
% Sem = init(Count) returns a new semaphore `Sem' with its counter
% initialized to `Count'.
%
% This has been renamed to impure_init.
%
:- impure func init(int::in) = (semaphore::uo) is det.
:- pragma obsolete(init/1).
% signal(Sem, !IO) increments the counter associated with `Sem'
% and if the resulting counter has a value greater than 0, it wakes
% one or more threads that are waiting on this semaphore (if
% any).
%
:- pred signal(semaphore::in, io::di, io::uo) is det.
% wait(Sem, !IO) blocks until the counter associated with `Sem'
% becomes greater than 0, whereupon it wakes, decrements the
% counter and returns.
%
:- pred wait(semaphore::in, io::di, io::uo) is det.
% try_wait(Sem, Succ, !IO) is the same as wait/3, except that
% instead of blocking, it binds `Succ' to a boolean indicating
% whether the call succeeded in obtaining the semaphore or not.
%
:- pred try_wait(semaphore::in, bool::out, io::di, io::uo) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
init(Count, Semaphore, !IO) :-
promise_pure (
impure impure_init(Count, Semaphore)
).
init(Semaphore, !IO) :-
init(0, Semaphore, !IO).
impure_init(Count) = Semaphore :-
impure impure_init(Count, Semaphore).
init(Count) = Semaphore :-
impure impure_init(Count, Semaphore).
signal(Semaphore, !IO) :-
promise_pure (
impure impure_signal(Semaphore),
!:IO = !.IO
).
wait(Semaphore, !IO) :-
promise_pure (
impure impure_wait(Semaphore),
!:IO = !.IO
).
try_wait(Sem, Res, !IO) :-
promise_pure (
impure impure_try_wait(Sem, Res)
).
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- interface.
% The semaphore operations above can be used without the I/O state in impure
% code. These predicates are provided for use by implementors.
:- impure pred impure_init(int::in, semaphore::uo) is det.
:- impure pred impure_init(semaphore::uo) is det.
:- impure pred impure_wait(semaphore::in) is det.
:- impure pred impure_try_wait(semaphore::in, bool::out) is det.
:- impure pred impure_signal(semaphore::in) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- pragma foreign_decl("C", "
#include <stdio.h>
#include ""mercury_context.h""
#include ""mercury_thread.h""
typedef struct ML_SEMAPHORE_STRUCT {
MR_Integer count;
#ifndef MR_HIGHLEVEL_CODE
MR_Context *suspended_head;
MR_Context *suspended_tail;
#else
#ifdef MR_THREAD_SAFE
MercuryCond cond;
#endif
#endif
#ifdef MR_THREAD_SAFE
MercuryLock lock;
#endif
} ML_Semaphore;
").
:- pragma foreign_code("C#", "
public class ML_Semaphore {
public int count;
};
").
% XXX the struct tag works around bug #19 in high-level C grades
:- pragma foreign_type("C", semaphore, "struct ML_SEMAPHORE_STRUCT *",
[can_pass_as_mercury_type]).
:- pragma foreign_type("C#", semaphore, "thread__semaphore.ML_Semaphore").
:- pragma foreign_type("Erlang", semaphore, "").
:- pragma foreign_type("Java", semaphore, "jmercury.runtime.Semaphore").
:- pragma foreign_decl("C", "
extern void
ML_finalize_semaphore(void *obj, void *cd);
").
%---------------------------------------------------------------------------%
impure_init(Semaphore) :-
impure impure_init(0, Semaphore).
:- pragma foreign_proc("C",
impure_init(Count::in, Semaphore::uo),
[will_not_call_mercury, thread_safe],
"
ML_Semaphore *sem;
MR_incr_hp_type_msg(sem, ML_Semaphore,
MR_ALLOC_ID, ""thread.semaphore.semaphore/0"");
sem->count = Count;
#ifndef MR_HIGHLEVEL_CODE
sem->suspended_head = NULL;
sem->suspended_tail = NULL;
#else
#ifdef MR_THREAD_SAFE
pthread_cond_init(&(sem->cond), MR_COND_ATTR);
#endif
#endif
#ifdef MR_THREAD_SAFE
pthread_mutex_init(&(sem->lock), MR_MUTEX_ATTR);
#endif
/*
** The condvar and the mutex will need to be destroyed
** when the semaphore is garbage collected.
*/
MR_GC_register_finalizer(sem, ML_finalize_semaphore, NULL);
Semaphore = sem;
").
:- pragma foreign_proc("C#",
impure_init(Count::in, Semaphore::uo),
[will_not_call_mercury, thread_safe],
"
Semaphore = new thread__semaphore.ML_Semaphore();
Semaphore.count = Count;
").
:- pragma foreign_proc("Java",
impure_init(Count::in, Semaphore::uo),
[will_not_call_mercury, thread_safe],
"
Semaphore = new jmercury.runtime.Semaphore(Count);
").
:- pragma foreign_code("C", "
void
ML_finalize_semaphore(void *obj, void *cd)
{
ML_Semaphore *sem;
sem = (ML_Semaphore *) obj;
#if defined(MR_THREAD_SAFE)
#if defined(MR_HIGHLEVEL_CODE)
pthread_cond_destroy(&(sem->cond));
#endif
pthread_mutex_destroy(&(sem->lock));
#endif
}
").
%---------------------------------------------------------------------------%
% impure_signal causes the calling context to resume in semaphore.nop,
% which simply jumps to the succip. That will return control to the caller
% of semaphore.signal as intended, but not if this procedure is inlined.
%
% XXX get rid of this limitation at some stage.
%
:- pragma no_inline(semaphore.impure_signal/1).
:- pragma foreign_proc("C",
impure_signal(Semaphore::in),
[will_not_call_mercury, thread_safe],
"
ML_Semaphore *sem;
#ifndef MR_HIGHLEVEL_CODE
MR_Context *ctxt;
#endif
sem = (ML_Semaphore *) Semaphore;
MR_LOCK(&(sem->lock), ""semaphore.signal"");
#ifndef MR_HIGHLEVEL_CODE
if (sem->count >= 0 && sem->suspended_head != NULL) {
/* Reschedule the context at the start of the queue. */
ctxt = sem->suspended_head;
sem->suspended_head = ctxt->MR_ctxt_next;
if (sem->suspended_tail == ctxt) {
sem->suspended_tail = ctxt->MR_ctxt_next;
assert(sem->suspended_tail == NULL);
}
MR_UNLOCK(&(sem->lock), ""semaphore.signal"");
MR_schedule_context(ctxt);
/* yield() */
/* This context switch can be done more directly and faster */
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__semaphore__nop);
#else
MR_ENGINE(MR_eng_this_context)->MR_ctxt_resume =
&&signal_skip_to_the_end_1;
#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
signal_skip_to_the_end_1: ;
#endif
} else {
sem->count++;
MR_UNLOCK(&(sem->lock), ""semaphore.signal"");
/* yield() */
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__semaphore__nop);
#else
MR_ENGINE(MR_eng_this_context)->MR_ctxt_resume =
&&signal_skip_to_the_end_2;
#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
signal_skip_to_the_end_2: ;
#endif
}
#else
sem->count++;
MR_COND_SIGNAL(&(sem->cond), ""semaphore.signal"");
MR_UNLOCK(&(sem->lock), ""semaphore.signal"");
#endif
").
:- pragma foreign_proc("C#",
impure_signal(Semaphore::in),
[will_not_call_mercury, thread_safe],
"
System.Threading.Monitor.Enter(Semaphore);
Semaphore.count++;
// XXX I think we only need to do a Pulse.
System.Threading.Monitor.PulseAll(Semaphore);
System.Threading.Monitor.Exit(Semaphore);
").
:- pragma foreign_proc("Java",
impure_signal(Semaphore::in),
[will_not_call_mercury, thread_safe],
"
Semaphore.release();
").
%---------------------------------------------------------------------------%
% impure_wait causes the calling context to resume in semaphore.nop,
% which simply jumps to the succip. That will return control to the caller
% of semaphore.wait as intended, but not if this procedure is inlined.
%
% XXX get rid of this limitation at some stage.
%
:- pragma no_inline(impure_wait/1).
:- pragma foreign_proc("C",
impure_wait(Semaphore::in),
[will_not_call_mercury, thread_safe],
"
ML_Semaphore *sem;
#ifndef MR_HIGHLEVEL_CODE
MR_Context *ctxt;
#endif
sem = (ML_Semaphore *) Semaphore;
MR_LOCK(&(sem->lock), ""semaphore.wait"");
#ifndef MR_HIGHLEVEL_CODE
if (sem->count > 0) {
sem->count--;
MR_UNLOCK(&(sem->lock), ""semaphore.wait"");
} else {
MR_save_context(MR_ENGINE(MR_eng_this_context));
/* Put the current context at the end of the queue. */
ctxt = MR_ENGINE(MR_eng_this_context);
#ifdef ML_THREAD_AVOID_LABEL_ADDRS
ctxt->MR_ctxt_resume = MR_ENTRY(mercury__thread__semaphore__nop);
#else
ctxt->MR_ctxt_resume = &&wait_skip_to_the_end;
#endif
ctxt->MR_ctxt_next = NULL;
if (sem->suspended_tail) {
sem->suspended_tail->MR_ctxt_next = ctxt;
sem->suspended_tail = ctxt;
} else {
sem->suspended_head = ctxt;
sem->suspended_tail = ctxt;
}
MR_UNLOCK(&(sem->lock), ""semaphore.wait"");
/* Make the current engine do something else. */
MR_ENGINE(MR_eng_this_context) = NULL;
MR_idle();
#ifndef ML_THREAD_AVOID_LABEL_ADDRS
wait_skip_to_the_end: ;
#endif
}
#else
while (sem->count <= 0) {
/*
** Although it goes against the spec, pthread_cond_wait() can
** return prematurely with the error code EINTR in glibc 2.3.2
** if the thread is sent a signal.
*/
while (MR_COND_WAIT(&(sem->cond), &(sem->lock), ""semaphore.wait"") != 0) {
/* do nothing */
}
}
sem->count--;
MR_UNLOCK(&(sem->lock), ""semaphore.wait"");
#endif
").
:- pragma foreign_proc("C#",
impure_wait(Semaphore::in),
[will_not_call_mercury, thread_safe],
"
System.Threading.Monitor.Enter(Semaphore);
while (Semaphore.count <= 0) {
System.Threading.Monitor.Wait(Semaphore);
}
Semaphore.count--;
System.Threading.Monitor.Exit(Semaphore);
").
:- pragma foreign_proc("Java",
impure_wait(Semaphore::in),
[will_not_call_mercury, thread_safe],
"
/*
** acquire() might be useful as well; it will throw an exception if the
** thread is interrupted.
*/
Semaphore.acquireUninterruptibly();
").
%---------------------------------------------------------------------------%
impure_try_wait(Sem, Res) :-
impure impure_try_wait_2(Sem, Res0),
Res = ( if Res0 = 0 then yes else no ).
:- impure pred impure_try_wait_2(semaphore::in, int::out) is det.
:- pragma foreign_proc("C",
impure_try_wait_2(Semaphore::in, Res::out),
[will_not_call_mercury, thread_safe],
"
ML_Semaphore *sem;
sem = (ML_Semaphore *) Semaphore;
MR_LOCK(&(sem->lock), ""semaphore.try_wait"");
if (sem->count > 0) {
sem->count--;
MR_UNLOCK(&(sem->lock), ""semaphore.try_wait"");
Res = 0;
} else {
MR_UNLOCK(&(sem->lock), ""semaphore.try_wait"");
Res = 1;
}
").
:- pragma foreign_proc("C#",
impure_try_wait_2(Semaphore::in, Res::out),
[will_not_call_mercury, thread_safe],
"
if (System.Threading.Monitor.TryEnter(Semaphore)) {
if (Semaphore.count > 0) {
Semaphore.count--;
System.Threading.Monitor.Exit(Semaphore);
Res = 0;
} else {
System.Threading.Monitor.Exit(Semaphore);
Res = 1;
}
} else {
Res = 1;
}
").
:- pragma foreign_proc("Java",
impure_try_wait_2(Semaphore::in, Res::out),
[will_not_call_mercury, thread_safe],
"
Res = Semaphore.tryAcquire() ? 0 : 1;
").
%---------------------------------------------------------------------------%
:- pragma foreign_decl("C",
"
/*
INIT mercury_sys_init_semaphore_modules
*/
#ifndef MR_HIGHLEVEL_CODE
MR_define_extern_entry(mercury__thread__semaphore__nop);
#endif
").
:- pragma foreign_code("C",
"
#ifndef MR_HIGHLEVEL_CODE
MR_BEGIN_MODULE(hand_written_semaphores_module)
MR_init_entry_ai(mercury__thread__semaphore__nop);
MR_BEGIN_CODE
MR_define_entry(mercury__thread__semaphore__nop);
{
MR_proceed();
}
MR_END_MODULE
#endif
/* forward decls to suppress gcc warnings */
void mercury_sys_init_semaphore_modules_init(void);
void mercury_sys_init_semaphore_modules_init_type_tables(void);
#ifdef MR_DEEP_PROFILING
void mercury_sys_init_semaphore_modules_write_out_proc_statics(
FILE *deep_fp, FILE *procrep_fp);
#endif
void mercury_sys_init_semaphore_modules_init(void)
{
#ifndef MR_HIGHLEVEL_CODE
hand_written_semaphores_module();
#endif
}
void mercury_sys_init_semaphore_modules_init_type_tables(void)
{
/* no types to register */
}
#ifdef MR_DEEP_PROFILING
void mercury_sys_init_semaphore_modules_write_out_proc_statics(
FILE *deep_fp, FILE *procrep_fp)
{
/* no proc_statics to write out */
}
#endif
").
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%