From 38ad47b45ee1fffc1eb2fa1bdd5527bf942a7322 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Fri, 10 Mar 2017 09:38:22 +0000 Subject: [PATCH 01/27] Fix broken link. Copied from Perforce Change: 192963 ServerID: perforce.ravenbrook.com --- mps/manual/source/mmref/lang.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mps/manual/source/mmref/lang.rst b/mps/manual/source/mmref/lang.rst index 7462f89742b..19dab83e7ba 100644 --- a/mps/manual/source/mmref/lang.rst +++ b/mps/manual/source/mmref/lang.rst @@ -539,7 +539,7 @@ Memory management in various languages .. link:: - `Borland Delphi Home Page `_, + `Embarcadero (formely Borland) Delphi `_, `Pascal standardization `_. Perl From 36955b7bcbc8bc34cfd246dafa98873ca3fd8858 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Fri, 3 Nov 2017 14:34:20 +0000 Subject: [PATCH 02/27] Platforms fri3ll and fri6ll are supported, so add them to overview. Copied from Perforce Change: 193383 ServerID: perforce.ravenbrook.com --- mps/manual/source/guide/overview.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mps/manual/source/guide/overview.rst b/mps/manual/source/guide/overview.rst index ff418b27de0..dc7652af1c9 100644 --- a/mps/manual/source/guide/overview.rst +++ b/mps/manual/source/guide/overview.rst @@ -53,7 +53,7 @@ The MPS is currently supported for deployment on: - Linux 2.6 or later, on IA-32 using GCC and on x86-64 using GCC or Clang/LLVM; -- FreeBSD 7 or later, on IA-32 and x86-64, using GCC; +- FreeBSD 7 or later, on IA-32 and x86-64, using GCC or Clang/LLVM; - OS X 10.4 or later, on IA-32 and x86-64, using Clang/LLVM. From 1a73a6e986452ed0b3e01875c8c2a0819301d7d9 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 21 Dec 2017 14:04:47 +0000 Subject: [PATCH 03/27] Format methods must be async-signal-safe on posix. Copied from Perforce Change: 193472 --- mps/manual/source/topic/format.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mps/manual/source/topic/format.rst b/mps/manual/source/topic/format.rst index ccf6c27ec65..9210a260d16 100644 --- a/mps/manual/source/topic/format.rst +++ b/mps/manual/source/topic/format.rst @@ -228,7 +228,8 @@ Cautions Therefore, the format methods must be able to be run at any time, including asynchronously or in parallel with the rest of the - program. + program. On POSIX systems, this means that format methods must be + async-signal-safe. #. Format methods must be re-entrant. From 4e5c8c54ce1c22fac146e3796846c3a098e88338 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Mon, 12 Feb 2018 16:39:16 +0000 Subject: [PATCH 04/27] Avoid warning about duplicate definition of class functin. Copied from Perforce Change: 193549 --- mps/design/cbs.txt | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/mps/design/cbs.txt b/mps/design/cbs.txt index 42e4e23c09c..132087b8c07 100644 --- a/mps/design/cbs.txt +++ b/mps/design/cbs.txt @@ -68,24 +68,18 @@ is typically embedded in another structure. External classes ................ -``CLASS(CBS)`` +_`.class.cbs`: ``CLASS(CBS)`` is the CBS class, a subclass of +``CLASS(Land)`` suitable for passing to ``LandInit()``. -_`.class.cbs`: The CBS class, a subclass of ``CLASS(Land)`` suitable -for passing to ``LandInit()``. - -``CLASS(CBSFast)`` - -_`.class.fast`: A subclass of ``CLASS(CBS)`` that maintains, for each -subtree, the size of the largest block in that subtree. This enables -the ``LandFindFirst()``, ``LandFindLast()``, and ``LandFindLargest()`` -generic functions. - -``CLASS(CBSZoned)`` - -_`.class.zoned`: A subclass of ``CLASS(CBSFast)`` that maintains, for -each subtree, the union of the zone sets of all ranges in that -subtree. This enables the ``LandFindInZones()`` generic function. +_`.class.fast`: ``CLASS(CBSFast)`` is subclass of ``CLASS(CBS)`` that +maintains, for each subtree, the size of the largest block in that +subtree. This enables the ``LandFindFirst()``, ``LandFindLast()``, and +``LandFindLargest()`` generic functions. +_`.class.zoned`: ``CLASS(CBSZoned)`` is a subclass of +``CLASS(CBSFast)`` that maintains, for each subtree, the union of the +zone sets of all ranges in that subtree. This enables the +``LandFindInZones()`` generic function. Keyword arguments @@ -279,7 +273,7 @@ Document History Copyright and License --------------------- -Copyright © 1998-2016 Ravenbrook Limited. All rights reserved. +Copyright © 1998-2018 Ravenbrook Limited. All rights reserved. . This is an open source license. Contact Ravenbrook for commercial licensing options. From 8b5079d23b357863b6b2fbedb574f6ad129218c1 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Mon, 12 Feb 2018 16:49:42 +0000 Subject: [PATCH 05/27] Make mps manual sphinx extensions compatible with recent versions of sphinx: 1. Don't use the html_use_smartypants directive (smartquotes are on by default now). 2. Don't call make_admonition but inherit from BaseAdmonition instead. 3. Set the title of an admonition via the visit method instead of hacking the title node (which is now non-existent as a result of item 2). Copied from Perforce Change: 193550 --- mps/manual/source/conf.py | 4 - mps/manual/source/extensions/mps/__init__.py | 129 +++++++++---------- 2 files changed, 60 insertions(+), 73 deletions(-) diff --git a/mps/manual/source/conf.py b/mps/manual/source/conf.py index 733438fc253..28c757e0ac5 100644 --- a/mps/manual/source/conf.py +++ b/mps/manual/source/conf.py @@ -144,10 +144,6 @@ html_static_path = ['images'] # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -html_use_smartypants = True - # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} diff --git a/mps/manual/source/extensions/mps/__init__.py b/mps/manual/source/extensions/mps/__init__.py index b5797dc0796..466aaef3359 100644 --- a/mps/manual/source/extensions/mps/__init__.py +++ b/mps/manual/source/extensions/mps/__init__.py @@ -6,17 +6,40 @@ See from collections import defaultdict from inspect import isabstract, isclass import re -from . import designs +import warnings from docutils import nodes, transforms +from docutils.parsers.rst import Directive +from docutils.parsers.rst.directives.admonitions import BaseAdmonition from sphinx import addnodes from sphinx.directives.other import VersionChange from sphinx.domains import Domain from sphinx.roles import XRefRole -from sphinx.util.compat import Directive, make_admonition from sphinx.util.nodes import set_source_info, process_index_entry -from sphinx.locale import versionlabels -versionlabels['deprecatedstarting'] = 'Deprecated starting with version %s' +from sphinx.locale import admonitionlabels, versionlabels + +from . import designs + +versionlabels['deprecatedstarting'] = "Deprecated starting with version %s" +admonitionlabels.update( + aka="Also known as", + bibref="Related publication", + bibrefs="Related publications", + deprecated="Deprecated", + historical="Historical note", + link="Related link", + links="Related links", + note="Note", + notes="Notes", + opposite="Opposite term", + opposites="Opposite terms", + relevance="Relevance to memory management", + see="See", + similar="Similar term", + similars="Similar terms", + specific="In the MPS", + topics="Topic", + topicss="Topics"), class MpsDomain(Domain): label = 'MPS' @@ -25,12 +48,15 @@ class MpsDomain(Domain): class MpsDirective(Directive): @classmethod def add_to_app(cls, app): - if hasattr(cls, 'name'): name = cls.name - elif hasattr(cls, 'nodecls'): name = cls.nodecls.__name__ - else: return - if hasattr(cls, 'nodecls') and hasattr(cls, 'visit'): - app.add_node(cls.nodecls, html = cls.visit, latex = cls.visit, - text = cls.visit, man = cls.visit) + if hasattr(cls, 'name'): + name = cls.name + elif hasattr(cls, 'node_class') and cls.node_class is not None: + name = cls.node_class.__name__ + else: + return + if hasattr(cls, 'node_class') and hasattr(cls, 'visit'): + app.add_node(cls.node_class, html=cls.visit, latex=cls.visit, + text=cls.visit, man=cls.visit) if hasattr(cls, 'domain'): app.add_directive_to_domain(cls.domain, name, cls) else: @@ -82,143 +108,110 @@ def mps_ref_role(name, rawtext, text, lineno, inliner, options={}, content=[]): return [refnode], [] class Admonition(nodes.Admonition, nodes.Element): - pass + plural = False def visit_admonition_node(self, node): - self.visit_admonition(node) + name = type(node).__name__ + ('s' if node.plural else '') + self.visit_admonition(node, name=name) def depart_admonition_node(self, node): self.depart_admonition(node) -class AdmonitionDirective(MpsDirective): - label = 'Admonition' +class AdmonitionDirective(MpsDirective, BaseAdmonition): has_content = True visit = visit_admonition_node, depart_admonition_node - @classmethod - def add_to_app(cls, app): - if not hasattr(cls, 'nodecls'): return - super(AdmonitionDirective, cls).add_to_app(app) - - def run(self): - ad = make_admonition(self.nodecls, self.name, [self.label], - self.options, self.content, self.lineno, - self.content_offset, self.block_text, - self.state, self.state_machine) - return ad - class PluralDirective(AdmonitionDirective): def run(self): ad = super(PluralDirective, self).run() - refs = sum(1 for node in ad[0][1] - if isinstance(node, addnodes.pending_xref) - or isinstance(node, nodes.Referential)) + refs = sum(1 for node in ad[0][0] + if isinstance(node, (addnodes.pending_xref, + nodes.Referential))) if refs > 1: - assert(isinstance(ad[0][0], nodes.title)) - ad[0][0][0] = nodes.Text(self.plural) + ad[0].plural = True return ad class aka(Admonition): pass class AkaDirective(AdmonitionDirective): - nodecls = aka - label = 'Also known as' + node_class = aka class bibref(Admonition): pass class BibrefDirective(PluralDirective): - nodecls = bibref - label = 'Related publication' - plural = 'Related publications' + node_class = bibref class deprecated(Admonition): pass class DeprecatedDirective(AdmonitionDirective): - nodecls = deprecated - label = 'Deprecated' + node_class = deprecated class historical(Admonition): pass class HistoricalDirective(AdmonitionDirective): - nodecls = historical - label = 'Historical note' + node_class = historical class link(Admonition): pass class LinkDirective(PluralDirective): - nodecls = link - label = 'Related link' - plural = 'Related links' + node_class = link class note(Admonition): pass class NoteDirective(AdmonitionDirective): - nodecls = note - label = 'Note' - plural = 'Notes' + node_class = note def run(self): ad = super(NoteDirective, self).run() - assert(isinstance(ad[0][0], nodes.title)) - if len(ad[0]) == 1: return ad - if (isinstance(ad[0][1], nodes.enumerated_list) - and sum(1 for _ in ad[0][1].traverse(nodes.list_item)) > 1 - or isinstance(ad[0][1], nodes.footnote) + if (isinstance(ad[0][0], nodes.enumerated_list) + and sum(1 for _ in ad[0][0].traverse(nodes.list_item)) > 1 + or isinstance(ad[0][0], nodes.footnote) and sum(1 for _ in ad[0].traverse(nodes.footnote)) > 1): - ad[0][0][0] = nodes.Text(self.plural) + ad[0].plural = True return ad class opposite(Admonition): pass class OppositeDirective(PluralDirective): - nodecls = opposite - label = 'Opposite term' - plural = 'Opposite terms' + node_class = opposite class relevance(Admonition): pass class RelevanceDirective(AdmonitionDirective): - nodecls = relevance - label = 'Relevance to memory management' + node_class = relevance class see(Admonition): pass class SeeDirective(AdmonitionDirective): - nodecls = see - label = 'See' + node_class = see class similar(Admonition): pass class SimilarDirective(PluralDirective): - nodecls = similar - label = 'Similar term' - plural = 'Similar terms' + node_class = similar class specific(Admonition): pass class SpecificDirective(AdmonitionDirective): domain = 'mps' - nodecls = specific - label = 'In the MPS' + node_class = specific class topics(Admonition): pass class TopicsDirective(PluralDirective): - nodecls = topics - label = 'Topic' - plural = 'Topics' + node_class = topics class GlossaryTransform(transforms.Transform): """ @@ -329,8 +322,6 @@ class GlossaryTransform(transforms.Transform): print('{}:{}: WARNING: cross-reference to {}.' .format(doc, line, i)) - - def setup(app): designs.convert_updated(app) app.add_domain(MpsDomain) From f294e06f1c665c16de9d44e5f189b6d702a5b260 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 13 Jun 2018 10:56:47 +0100 Subject: [PATCH 06/27] Add cross-references from mps_arena_has_addr to mps_addr_pool (and vice versa), to decrease the likelihood that developers will miss these functions. Copied from Perforce Change: 193721 --- mps/manual/source/topic/arena.rst | 7 +++++++ mps/manual/source/topic/pool.rst | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/mps/manual/source/topic/arena.rst b/mps/manual/source/topic/arena.rst index cd9b2e7a970..b77e94c8ecb 100644 --- a/mps/manual/source/topic/arena.rst +++ b/mps/manual/source/topic/arena.rst @@ -1024,3 +1024,10 @@ Arena introspection and debugging return storage to the operating system). For reliable results call this function and interpret the result while the arena is in the :term:`parked state`. + + .. seealso:: + + To find out which :term:`pool` the address belongs to, use + :c:func:`mps_addr_pool`, and to find out which :term:`object + format` describes the object at the address, use + :c:func:`mps_addr_fmt`. diff --git a/mps/manual/source/topic/pool.rst b/mps/manual/source/topic/pool.rst index d1ffe56d45b..360911494c5 100644 --- a/mps/manual/source/topic/pool.rst +++ b/mps/manual/source/topic/pool.rst @@ -163,3 +163,10 @@ Pool introspection may immediately become invalidated. For reliable results call this function and interpret the result while the arena is in the :term:`parked state`. + + .. seealso:: + + To find out which :term:`object format` describes the object + at the address, use :c:func:`mps_addr_fmt`. If you only care + whether the address belongs to a particular :term:`arena`, use + :c:func:`mps_arena_has_addr`. From 9de5c1dfbed73c2a249637ee33d1e05722837d48 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 13 Jun 2018 13:59:14 +0100 Subject: [PATCH 07/27] Avoid using deprecated function mps_fix and deprecated macro mps_fix in the test cases; use mps_fix12 instead. Copied from Perforce Change: 193726 --- mps/code/eventpy.c | 28 ++++++++++++++-------------- mps/code/fmtdy.c | 18 ++++++++++++------ mps/code/mpsi.c | 6 +++--- mps/design/critical-path.txt | 4 ++-- mps/test/argerr/146.c | 4 +++- mps/test/argerr/147.c | 4 +++- mps/test/argerr/148.c | 4 +++- mps/test/argerr/149.c | 2 +- mps/test/argerr/150.c | 2 +- mps/test/argerr/151.c | 2 +- mps/test/test/testlib/awlfmt.c | 4 ++-- mps/test/test/testlib/exfmt.c | 4 ++-- mps/test/test/testlib/fastfmt.c | 4 ++-- mps/test/test/testlib/myfmt.c | 4 ++-- mps/test/test/testlib/newfmt.c | 2 +- mps/test/test/testlib/rankfmt.c | 4 ++-- 16 files changed, 54 insertions(+), 42 deletions(-) diff --git a/mps/code/eventpy.c b/mps/code/eventpy.c index 23d14c5f63c..feac0efefb5 100644 --- a/mps/code/eventpy.c +++ b/mps/code/eventpy.c @@ -1,7 +1,7 @@ /* eventpy.c: GENERATE PYTHON INTERFACE TO EVENTS * * $Id$ - * Copyright (c) 2016 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2016-2018 Ravenbrook Limited. See end of file for license. * * This command-line program emits Python data structures that can be * used to parse an event stream in text format (as output by the @@ -22,37 +22,37 @@ int main(int argc, char *argv[]) printf("__version__ = %d, %d, %d\n", EVENT_VERSION_MAJOR, EVENT_VERSION_MEDIAN, EVENT_VERSION_MINOR); - puts("EventKind = namedtuple('EventKind', 'name code doc')"); - puts("class kind:"); + puts("KindDesc = namedtuple('KindDesc', 'name code doc')"); + puts("class Kind:"); #define ENUM(_, NAME, DOC) \ - printf(" " #NAME " = EventKind('" #NAME "', %d, \"%s\")\n", \ + printf(" " #NAME " = KindDesc('" #NAME "', %d, \"%s\")\n", \ EventKind ## NAME, DOC); EventKindENUM(ENUM, _); #undef ENUM - puts("kinds = {"); + puts("KIND = {"); #define ENUM(_, NAME, _1) \ - printf(" %d: kind." #NAME ",\n", EventKind ## NAME); + printf(" %d: Kind." #NAME ",\n", EventKind ## NAME); EventKindENUM(ENUM, _); #undef ENUM puts("}"); - puts("EventParam = namedtuple('EventParam', 'sort, name')"); - puts("Event = namedtuple('Event', 'name code always kind params')"); - puts("class event:"); + puts("EventParam = namedtuple('EventParam', 'sort name')"); + puts("EventDesc = namedtuple('EventDesc', 'name code always kind params')"); + puts("class Event:"); #define EVENT_PARAM(X, INDEX, SORT, NAME) \ puts(" EventParam('" #SORT "', '" #NAME "'),"); #define EVENT_DEFINE(X, NAME, CODE, ALWAYS, KIND) \ - printf(" " #NAME " = Event('" #NAME "', %d, %s, kind." #KIND ", [\n", \ + printf(" " #NAME " = EventDesc('" #NAME "', %d, %s, Kind." #KIND ", [\n", \ CODE, ALWAYS ? "True" : "False"); \ EVENT_ ## NAME ## _PARAMS(EVENT_PARAM, X); \ - puts(" ]);"); + puts(" ])"); EVENT_LIST(EVENT_DEFINE, 0); #undef EVENT - puts("events = {"); + puts("EVENT = {"); #define EVENT_ITEM(X, NAME, CODE, ALWAYS, KIND) \ - printf(" %d: event." #NAME ",\n", CODE); + printf(" %d: Event." #NAME ",\n", CODE); EVENT_LIST(EVENT_ITEM, 0); #undef EVENT puts("}"); @@ -63,7 +63,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2016 Ravenbrook Limited . + * Copyright (c) 2016-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/fmtdy.c b/mps/code/fmtdy.c index dee04c673a4..4457d637a59 100644 --- a/mps/code/fmtdy.c +++ b/mps/code/fmtdy.c @@ -1,7 +1,7 @@ /* fmtdy.c: DYLAN OBJECT FORMAT IMPLEMENTATION * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. * * .readership: MPS developers, Dylan developers @@ -407,8 +407,11 @@ extern mps_res_t dylan_scan1(mps_ss_t mps_ss, mps_addr_t *object_io) return MPS_RES_OK; } - res = mps_fix(mps_ss, p); /* fix the wrapper */ - if ( res != MPS_RES_OK ) return res; + MPS_SCAN_BEGIN(mps_ss) { + res = MPS_FIX12(mps_ss, p); /* fix the wrapper */ + } MPS_SCAN_END(mps_ss); + if (res != MPS_RES_OK) + return res; w = (mps_word_t *)p[0]; /* wrapper is header word */ assert(dylan_wrapper_check(w)); @@ -567,8 +570,11 @@ extern mps_res_t dylan_scan1_weak(mps_ss_t mps_ss, mps_addr_t *object_io) assert((h & 3) == 0); unused(h); - res = mps_fix(mps_ss, p); - if ( res != MPS_RES_OK ) return res; + MPS_SCAN_BEGIN(mps_ss) { + res = MPS_FIX12(mps_ss, p); + } MPS_SCAN_END(mps_ss); + if (res != MPS_RES_OK) + return res; /* w points to wrapper */ w = (mps_word_t *)p[0]; @@ -852,7 +858,7 @@ mps_res_t dylan_fmt_weak(mps_fmt_t *mps_fmt_o, mps_arena_t arena) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/mpsi.c b/mps/code/mpsi.c index b01826bf39f..c6b27845bad 100644 --- a/mps/code/mpsi.c +++ b/mps/code/mpsi.c @@ -1,7 +1,7 @@ /* mpsi.c: MEMORY POOL SYSTEM C INTERFACE LAYER * * $Id$ - * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. * * .purpose: This code bridges between the MPS interface to C, @@ -1697,7 +1697,7 @@ mps_res_t mps_fix(mps_ss_t mps_ss, mps_addr_t *ref_io) mps_res_t res; MPS_SCAN_BEGIN(mps_ss) { - res = MPS_FIX(mps_ss, ref_io); + res = MPS_FIX12(mps_ss, ref_io); } MPS_SCAN_END(mps_ss); return res; @@ -2159,7 +2159,7 @@ void _mps_args_set_key(mps_arg_s args[MPS_ARGS_MAX], unsigned i, /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2016 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/design/critical-path.txt b/mps/design/critical-path.txt index 6576ca3760e..8083e16b428 100644 --- a/mps/design/critical-path.txt +++ b/mps/design/critical-path.txt @@ -116,7 +116,7 @@ Very briefly, the critical path consists of five stages: write a good scanner. Then that could be linked from here. #. The first-stage fix, which filters out pointers inline in the - scanner. This is implemented in ``MPS_FIX()`` macros in + scanner. This is implemented in the ``MPS_FIX1()`` macro in mps.h_. .. _mps.h: ../code/mps.h @@ -377,7 +377,7 @@ Document History Copyright and License --------------------- -Copyright © 2012-2014 Ravenbrook Limited. All rights reserved. +Copyright © 2012-2018 Ravenbrook Limited. All rights reserved. . This is an open source license. Contact Ravenbrook for commercial licensing options. diff --git a/mps/test/argerr/146.c b/mps/test/argerr/146.c index cc202c2601d..5cead98d9d8 100644 --- a/mps/test/argerr/146.c +++ b/mps/test/argerr/146.c @@ -221,7 +221,9 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) the pun would probably work fine almost everywhere) */ comment("About to fix with null scan state..."); - res = mps_fix(NULL, (mps_addr_t *) &p); + MPS_SCAN_BEGIN(ss) { + res = MPS_FIX12(NULL, (mps_addr_t *) &p); + } MPS_SCAN_END(ss); error("fix with NULL scan state"); if (res != MPS_RES_OK) return res; obj->data.ref[i].addr = p; diff --git a/mps/test/argerr/147.c b/mps/test/argerr/147.c index 84acc4e1d4f..72e2b34ad69 100644 --- a/mps/test/argerr/147.c +++ b/mps/test/argerr/147.c @@ -222,7 +222,9 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) the pun would probably work fine almost everywhere) */ comment("About to fix with unaligned scan state..."); - res = mps_fix(UNALIGNED, (mps_addr_t *) &p); + MPS_SCAN_BEGIN(ss) { + res = MPS_FIX12(UNALIGNED, (mps_addr_t *) &p); + } MPS_SCAN_END(ss); error("fix with unaligned scan state"); if (res != MPS_RES_OK) return res; obj->data.ref[i].addr = p; diff --git a/mps/test/argerr/148.c b/mps/test/argerr/148.c index 774137e2df6..b424ac05d18 100644 --- a/mps/test/argerr/148.c +++ b/mps/test/argerr/148.c @@ -221,7 +221,9 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) the pun would probably work fine almost everywhere) */ comment("About to fix with null pointer..."); - res = mps_fix(ss, NULL); + MPS_SCAN_BEGIN(ss) { + res = MPS_FIX2(ss, NULL); + } MPS_SCAN_END(ss); error("fix with null pointer"); if (res != MPS_RES_OK) return res; obj->data.ref[i].addr = p; diff --git a/mps/test/argerr/149.c b/mps/test/argerr/149.c index c86d9ce3983..0f2fbf58b75 100644 --- a/mps/test/argerr/149.c +++ b/mps/test/argerr/149.c @@ -255,7 +255,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) */ comment("About to fix with null pointer..."); p = NULL; - res = mps_fix(ss, (mps_addr_t *) &p); + res = MPS_FIX12(ss, (mps_addr_t *) &p); error("fix with null pointer"); if (res != MPS_RES_OK) return res; obj->data.ref[i].addr = p; diff --git a/mps/test/argerr/150.c b/mps/test/argerr/150.c index 88d751f5d4a..784932ec953 100644 --- a/mps/test/argerr/150.c +++ b/mps/test/argerr/150.c @@ -254,7 +254,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) the pun would probably work fine almost everywhere) */ comment("About to fix on unaligned addr..."); - res = mps_fix(ss, UNALIGNED); + res = MPS_FIX12(ss, (mps_addr_t *) UNALIGNED); error("unaligned fix"); if (res != MPS_RES_OK) return res; obj->data.ref[i].addr = p; diff --git a/mps/test/argerr/151.c b/mps/test/argerr/151.c index 19ed2169a93..cbe40d26d5a 100644 --- a/mps/test/argerr/151.c +++ b/mps/test/argerr/151.c @@ -255,7 +255,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) */ comment("About to fix with unaligned pointer..."); p = UNALIGNED; - res = mps_fix(ss, &p); + res = MPS_FIX2(ss, &p); error("fix with unaligned pointer"); if (res != MPS_RES_OK) return res; obj->data.ref[i].addr = p; diff --git a/mps/test/test/testlib/awlfmt.c b/mps/test/test/testlib/awlfmt.c index 352b6afcaac..0977d0de6dd 100644 --- a/mps/test/test/testlib/awlfmt.c +++ b/mps/test/test/testlib/awlfmt.c @@ -231,7 +231,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) p = obj->data.assoc; if (p != NULL) { commentif(fixcomments, "fix %li[assoc]", obj->data.id); - res = MPS_FIX(ss, (mps_addr_t *) &p); + res = MPS_FIX12(ss, (mps_addr_t *) &p); if (res != MPS_RES_OK) return res; if (p == NULL) { commentif(deathcomments, "fixed %li[assoc] to NULL", obj->data.id); @@ -250,7 +250,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) */ commentif(fixcomments, "fix %li[%i] -> %li", obj->data.id, i, obj->data.ref[i].id); - res = MPS_FIX(ss, (mps_addr_t *) &p); + res = MPS_FIX12(ss, (mps_addr_t *) &p); if (p == NULL) { commentif(deathcomments, "fixed %li[%i] to NULL", obj->data.id, i); INCCOUNTIF(obj->data.countflag, DYING_REFERENCE_COUNT); diff --git a/mps/test/test/testlib/exfmt.c b/mps/test/test/testlib/exfmt.c index 687433eed20..2b2678834a5 100644 --- a/mps/test/test/testlib/exfmt.c +++ b/mps/test/test/testlib/exfmt.c @@ -217,7 +217,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) p = obj->data.assoc; if (p != NULL) { commentif(fixcomments, "fix %li[assoc]", obj->data.id); - res = MPS_FIX(ss, (mps_addr_t *) &p); + res = MPS_FIX12(ss, (mps_addr_t *) &p); if (res != MPS_RES_OK) return res; if (p == NULL) { commentif(deathcomments, "fixed %li[assoc] to NULL", obj->data.id); @@ -236,7 +236,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) */ commentif(fixcomments, "fix %li[%i] -> %li", obj->data.id, i, obj->data.ref[i].id); - res = MPS_FIX(ss, (mps_addr_t *) &p); + res = MPS_FIX12(ss, (mps_addr_t *) &p); if (p == NULL) { commentif(deathcomments, "fixed %li[%i] to NULL", obj->data.id, i); INCCOUNTIF(obj->data.countflag, DYING_REFERENCE_COUNT); diff --git a/mps/test/test/testlib/fastfmt.c b/mps/test/test/testlib/fastfmt.c index 80beda1f4a2..dcc01d494d2 100644 --- a/mps/test/test/testlib/fastfmt.c +++ b/mps/test/test/testlib/fastfmt.c @@ -151,7 +151,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) /* make sure to fix the assoc pointer first */ p = obj->data.assoc; if (p != NULL) { - res = MPS_FIX(ss, (mps_addr_t *) &p); + res = MPS_FIX12(ss, (mps_addr_t *) &p); if (res != MPS_RES_OK) return res; obj->data.assoc = p; } @@ -161,7 +161,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) p = obj->data.ref[i].addr; if (p != NULL) { - res = MPS_FIX(ss, (mps_addr_t *) &p); + res = MPS_FIX12(ss, (mps_addr_t *) &p); if (res != MPS_RES_OK) return res; obj->data.ref[i].addr = p; } diff --git a/mps/test/test/testlib/myfmt.c b/mps/test/test/testlib/myfmt.c index 2dd95617a56..768237c4165 100644 --- a/mps/test/test/testlib/myfmt.c +++ b/mps/test/test/testlib/myfmt.c @@ -103,7 +103,7 @@ mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) if (obj->ref[0] != NULL) { if (formatcomments) printf("Fix: %p.\n", (void*)&(obj->ref[0])); - res = MPS_FIX(ss, (mps_addr_t *) &(obj->ref[0])); /* pun! */ + res = MPS_FIX12(ss, (mps_addr_t *) &(obj->ref[0])); /* pun! */ if (res != MPS_RES_OK) { return res; @@ -112,7 +112,7 @@ mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) if (obj->ref[1] != NULL) { if (formatcomments) printf("Fix: %p.\n", (void*)&(obj->ref[1])); - res = MPS_FIX(ss, (mps_addr_t *) &(obj->ref[1])); /* pun! */ + res = MPS_FIX12(ss, (mps_addr_t *) &(obj->ref[1])); /* pun! */ if (res != MPS_RES_OK) { return res; diff --git a/mps/test/test/testlib/newfmt.c b/mps/test/test/testlib/newfmt.c index c82e96e40b9..0ccc35a5557 100644 --- a/mps/test/test/testlib/newfmt.c +++ b/mps/test/test/testlib/newfmt.c @@ -210,7 +210,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) */ commentif(fixcomments, "fix %li[%i] -> %li", obj->data.id, i, obj->data.ref[i].id); - res = MPS_FIX(ss, (mps_addr_t *) &p); + res = MPS_FIX12(ss, (mps_addr_t *) &p); if (res != MPS_RES_OK) return res; obj->data.ref[i].addr = p; } diff --git a/mps/test/test/testlib/rankfmt.c b/mps/test/test/testlib/rankfmt.c index 97c81da6c00..9226d1c6e94 100644 --- a/mps/test/test/testlib/rankfmt.c +++ b/mps/test/test/testlib/rankfmt.c @@ -362,7 +362,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) if (p != NULL) { commentif(fixcomments, "fix %li[assoc]", obj->data.id); q = p; - res = MPS_FIX(ss, (mps_addr_t *) &p); + res = MPS_FIX12(ss, (mps_addr_t *) &p); if (res != MPS_RES_OK) return res; if (p == NULL) { asserts(rank == mps_rank_weak(), @@ -387,7 +387,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) commentif(fixcomments, "fix %li[%i] -> %li", obj->data.id, i, obj->data.ref[i].id); q = p; - res = MPS_FIX(ss, (mps_addr_t *) &p); + res = MPS_FIX12(ss, (mps_addr_t *) &p); if (p == NULL) { asserts(rank == mps_rank_weak(), "non-weak reference fixed to NULL at %p[i]", obj); From a9b6bad3a06e9ee26fe800c1d0b148baea6c84f2 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 13 Jun 2018 14:06:46 +0100 Subject: [PATCH 08/27] Branching master to branch/2018-06-13/fork. Copied from Perforce Change: 193731 From ff8c75b0ec7e997df5a22b98f774bdf3d3e65227 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 13 Jun 2018 15:42:23 +0100 Subject: [PATCH 09/27] Fix typo in user guide: "stack_root" not "reg_root". Copied from Perforce Change: 193741 --- mps/manual/source/guide/lang.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/mps/manual/source/guide/lang.rst b/mps/manual/source/guide/lang.rst index 6ae1ce60aaf..324a6305563 100644 --- a/mps/manual/source/guide/lang.rst +++ b/mps/manual/source/guide/lang.rst @@ -976,8 +976,8 @@ as a root by calling :c:func:`mps_root_create_thread`:: void *marker = ▮ mps_root_t stack_root; - res = mps_root_create_thread(®_root, arena, thread, marker); - if (res != MPS_RES_OK) error("Couldn't create root"); + res = mps_root_create_thread(&stack_root, arena, thread, marker); + if (res != MPS_RES_OK) error("Couldn't create stack root"); In order to scan the control stack, the MPS needs to know where the :term:`cold end` of the stack is, and that's the role of the @@ -1175,13 +1175,13 @@ before destroying the arena, and so on. For example:: - mps_arena_park(arena); /* ensure no collection is running */ - mps_ap_destroy(obj_ap); /* destroy ap before pool */ - mps_pool_destroy(obj_pool); /* destroy pool before fmt */ - mps_root_destroy(reg_root); /* destroy root before thread */ - mps_thread_dereg(thread); /* deregister thread before arena */ - mps_fmt_destroy(obj_fmt); /* destroy fmt before arena */ - mps_arena_destroy(arena); /* last of all */ + mps_arena_park(arena); /* ensure no collection is running */ + mps_ap_destroy(obj_ap); /* destroy ap before pool */ + mps_pool_destroy(obj_pool); /* destroy pool before fmt */ + mps_root_destroy(stack_root); /* destroy root before thread */ + mps_thread_dereg(thread); /* deregister thread before arena */ + mps_fmt_destroy(obj_fmt); /* destroy fmt before arena */ + mps_arena_destroy(arena); /* last of all */ What next? From 81469706224a449c232fd9594f8c65942e5708bb Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 13 Jun 2018 17:09:07 +0100 Subject: [PATCH 10/27] Add fork test case (fails on os x if pthread_atfork is not called). pthread_atfork handlers on OS X: in the child, update the mach port for the forking thread and move all other threads to the dead ring. Copied from Perforce Change: 193746 --- mps/code/comm.gmk | 4 +++ mps/code/commpost.nmk | 3 ++ mps/code/commpre.nmk | 1 + mps/code/global.c | 10 ++++++ mps/code/mpm.h | 1 + mps/code/protxc.c | 66 ++++++++++++++++++++++++++++++++++++ mps/code/th.h | 7 ++++ mps/code/thxc.c | 76 +++++++++++++++++++++++++++++++++++++----- mps/tool/testcases.txt | 1 + 9 files changed, 160 insertions(+), 9 deletions(-) diff --git a/mps/code/comm.gmk b/mps/code/comm.gmk index 2d18d3f0e70..2b99fbb57e2 100644 --- a/mps/code/comm.gmk +++ b/mps/code/comm.gmk @@ -266,6 +266,7 @@ TEST_TARGETS=\ expt825 \ finalcv \ finaltest \ + forktest \ fotest \ gcbench \ landtest \ @@ -499,6 +500,9 @@ $(PFM)/$(VARIETY)/finalcv: $(PFM)/$(VARIETY)/finalcv.o \ $(PFM)/$(VARIETY)/finaltest: $(PFM)/$(VARIETY)/finaltest.o \ $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a +$(PFM)/$(VARIETY)/forktest: $(PFM)/$(VARIETY)/forktest.o \ + $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a + $(PFM)/$(VARIETY)/fotest: $(PFM)/$(VARIETY)/fotest.o \ $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a diff --git a/mps/code/commpost.nmk b/mps/code/commpost.nmk index 2e5a999744c..97f6733feb3 100644 --- a/mps/code/commpost.nmk +++ b/mps/code/commpost.nmk @@ -237,6 +237,9 @@ $(PFM)\$(VARIETY)\finalcv.exe: $(PFM)\$(VARIETY)\finalcv.obj \ $(PFM)\$(VARIETY)\finaltest.exe: $(PFM)\$(VARIETY)\finaltest.obj \ $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) +$(PFM)\$(VARIETY)\forktest.exe: $(PFM)\$(VARIETY)\forktest.obj \ + $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) + $(PFM)\$(VARIETY)\fotest.exe: $(PFM)\$(VARIETY)\fotest.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) diff --git a/mps/code/commpre.nmk b/mps/code/commpre.nmk index 8a79096f42c..b3007b86bb8 100644 --- a/mps/code/commpre.nmk +++ b/mps/code/commpre.nmk @@ -77,6 +77,7 @@ TEST_TARGETS=\ expt825.exe \ finalcv.exe \ finaltest.exe \ + forktest.exe \ fotest.exe \ gcbench.exe \ landtest.exe \ diff --git a/mps/code/global.c b/mps/code/global.c index 9b535ecb25f..a77a76d5622 100644 --- a/mps/code/global.c +++ b/mps/code/global.c @@ -100,6 +100,16 @@ static void arenaDenounce(Arena arena) } + +/* GlobalsArenaRing -- return the global ring of arenas */ + +Ring GlobalsArenaRing(void) +{ + AVER(arenaRingInit); + return &arenaRing; +} + + /* GlobalsCheck -- check the arena globals */ Bool GlobalsCheck(Globals arenaGlobals) diff --git a/mps/code/mpm.h b/mps/code/mpm.h index b21da8f0a5a..be6d949f0c7 100644 --- a/mps/code/mpm.h +++ b/mps/code/mpm.h @@ -492,6 +492,7 @@ extern Res GlobalsCompleteCreate(Globals arenaGlobals); extern void GlobalsPrepareToDestroy(Globals arenaGlobals); extern Res GlobalsDescribe(Globals arena, mps_lib_FILE *stream, Count depth); extern Ring GlobalsRememberedSummaryRing(Globals); +extern Ring GlobalsArenaRing(void); #define ArenaGlobals(arena) (&(arena)->globals) #define GlobalsArena(glob) PARENT(ArenaStruct, globals, glob) diff --git a/mps/code/protxc.c b/mps/code/protxc.c index 3edbfd8ddb4..674e2c70180 100644 --- a/mps/code/protxc.c +++ b/mps/code/protxc.c @@ -342,6 +342,69 @@ extern void ProtThreadRegister(Bool setup) } +/* atfork handlers -- support for fork() + * + * In order to support fork(), we need to solve the following problems: + * + * (1) the MPS lock might be held by another thread; + * + * (2.1) only the thread that called fork() exists in the child process; + * + * (2.2) in particular, the protection fault handling thread does not + * exist in the child process. + * + * (3) this thread has a new Mach port number in the child. + * + * TODO: what about protExcPort? + */ + +static void prot_atfork_prepare(void) +{ + Ring node, nextNode; + + /* Take all the locks, solving (1). */ + + /* For each arena, remember which thread is the current thread (if + any), solving (2.1). */ + RING_FOR(node, GlobalsArenaRing(), nextNode) { + Globals arenaGlobals = RING_ELT(Globals, globalRing, node); + Arena arena = GlobalsArena(arenaGlobals); + ThreadRingForkPrepare(ArenaThreadRing(arena), ArenaDeadRing(arena)); + } +} + +static void prot_atfork_parent(void) +{ + Ring node, nextNode; + /* Release all the locks in reverse order. */ + + /* For each arena, mark threads as not forking any more. */ + RING_FOR(node, GlobalsArenaRing(), nextNode) { + Globals arenaGlobals = RING_ELT(Globals, globalRing, node); + Arena arena = GlobalsArena(arenaGlobals); + ThreadRingForkParent(ArenaThreadRing(arena), ArenaDeadRing(arena)); + } +} + +static void prot_atfork_child(void) +{ + Ring node, nextNode; + /* For each arena, move all the threads to the dead ring, solving + (2.1), except for the thread that was marked as current by the + prepare handler, for which we update its mach port number, + solving (3). */ + RING_FOR(node, GlobalsArenaRing(), nextNode) { + Globals arenaGlobals = RING_ELT(Globals, globalRing, node); + Arena arena = GlobalsArena(arenaGlobals); + ThreadRingForkChild(ArenaThreadRing(arena), ArenaDeadRing(arena)); + } + + /* Restart the protection fault handling thread, solving (2.2). */ + + /* Release all the locks in reverse order, solving (1). */ +} + + /* ProtSetup -- set up protection exception handling */ static void protSetupInner(void) @@ -383,6 +446,9 @@ static void protSetupInner(void) AVER(pr == 0); if (pr != 0) fprintf(stderr, "ERROR: MPS pthread_create: %d\n", pr); /* .trans.must */ + + /* Install fork handlers. */ + pthread_atfork(prot_atfork_prepare, prot_atfork_parent, prot_atfork_child); } void ProtSetup(void) diff --git a/mps/code/th.h b/mps/code/th.h index 29a4d243d1d..efd6b53e040 100644 --- a/mps/code/th.h +++ b/mps/code/th.h @@ -73,6 +73,13 @@ extern Res ThreadScan(ScanState ss, Thread thread, Word *stackCold, void *closure); +/* ThreadRingFork* -- atfork handlers for Unix */ + +void ThreadRingForkPrepare(Ring threadRing, Ring deadRing); +void ThreadRingForkParent(Ring threadRing, Ring deadRing); +void ThreadRingForkChild(Ring threadRing, Ring deadRing); + + #endif /* th_h */ diff --git a/mps/code/thxc.c b/mps/code/thxc.c index 3ef92e63aa6..bf074b6ae99 100644 --- a/mps/code/thxc.c +++ b/mps/code/thxc.c @@ -37,6 +37,7 @@ typedef struct mps_thr_s { /* OS X / Mach thread structure */ Arena arena; /* owning arena */ RingStruct arenaRing; /* attaches to arena */ Bool alive; /* thread believed to be alive? */ + Bool forking; /* thread currently calling fork? */ thread_port_t port; /* thread kernel port */ } ThreadStruct; @@ -48,6 +49,7 @@ Bool ThreadCheck(Thread thread) CHECKL(thread->serial < thread->arena->threadSerial); CHECKD_NOSIG(Ring, &thread->arenaRing); CHECKL(BoolCheck(thread->alive)); + CHECKL(BoolCheck(thread->forking)); CHECKL(MACH_PORT_VALID(thread->port)); return TRUE; } @@ -80,6 +82,7 @@ Res ThreadRegister(Thread *threadReturn, Arena arena) thread->serial = arena->threadSerial; ++arena->threadSerial; thread->alive = TRUE; + thread->forking = FALSE; thread->port = mach_thread_self(); thread->sig = ThreadSig; AVERT(Thread, thread); @@ -99,6 +102,7 @@ void ThreadDeregister(Thread thread, Arena arena) { AVERT(Thread, thread); AVERT(Arena, arena); + AVER(!thread->forking); RingRemove(&thread->arenaRing); @@ -157,6 +161,16 @@ static Bool threadSuspend(Thread thread) return kern_return == KERN_SUCCESS; } + +/* ThreadRingSuspend -- suspend all threads on a ring, except the + * current one. + */ +void ThreadRingSuspend(Ring threadRing, Ring deadRing) +{ + mapThreadRing(threadRing, deadRing, threadSuspend); +} + + static Bool threadResume(Thread thread) { kern_return_t kern_return; @@ -169,15 +183,6 @@ static Bool threadResume(Thread thread) return kern_return == KERN_SUCCESS; } - -/* ThreadRingSuspend -- suspend all threads on a ring, except the - * current one. - */ -void ThreadRingSuspend(Ring threadRing, Ring deadRing) -{ - mapThreadRing(threadRing, deadRing, threadSuspend); -} - /* ThreadRingResume -- resume all threads on a ring, except the * current one. */ @@ -186,6 +191,59 @@ void ThreadRingResume(Ring threadRing, Ring deadRing) mapThreadRing(threadRing, deadRing, threadResume); } + +static Bool threadForkPrepare(Thread thread) +{ + AVER(!thread->forking); + thread->forking = (thread->port == mach_thread_self()); + return TRUE; +} + +/* ThreadRingForkPrepare -- prepare for a fork by marking the current + * thread as forking. + */ +void ThreadRingForkPrepare(Ring threadRing, Ring deadRing) +{ + mapThreadRing(threadRing, deadRing, threadForkPrepare); +} + + +static Bool threadForkParent(Thread thread) +{ + thread->forking = FALSE; + return TRUE; +} + +/* ThreadRingForkParent -- clear the forking flag in the parent after + * a fork. + */ +void ThreadRingForkParent(Ring threadRing, Ring deadRing) +{ + mapThreadRing(threadRing, deadRing, threadForkParent); +} + + +static Bool threadForkChild(Thread thread) +{ + if (thread->forking) { + thread->port = mach_thread_self(); + AVER(MACH_PORT_VALID(thread->port)); + thread->forking = FALSE; + return TRUE; + } else { + return FALSE; + } +} + +/* ThreadRingForkChild -- update the mach thread port for the current + * thread; move all other threads to the dead ring. + */ +void ThreadRingForkChild(Ring threadRing, Ring deadRing) +{ + mapThreadRing(threadRing, deadRing, threadForkChild); +} + + Thread ThreadRingThread(Ring threadRing) { Thread thread; diff --git a/mps/tool/testcases.txt b/mps/tool/testcases.txt index 52be04ded7f..1bce2ce7113 100644 --- a/mps/tool/testcases.txt +++ b/mps/tool/testcases.txt @@ -20,6 +20,7 @@ exposet0 =P expt825 finalcv =P finaltest =P +forktest =X fotest gcbench =N benchmark landtest From 2401957e62b37b94faf26cf4c2bb730915f483a3 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 14 Jun 2018 12:54:35 +0100 Subject: [PATCH 11/27] Extend forktest so that it does allocation before the fork and collection afterwards; this ensures that we hit the read barrier and thus exercise the exception handling. After forking in the child, re-create exception port and exception thread, and re-register the (only) thread with that port. Copied from Perforce Change: 193755 --- mps/code/forktest.c | 228 ++++++++++++++++++++++++++++++++++++++++++++ mps/code/global.c | 15 +-- mps/code/mpm.h | 2 +- mps/code/protxc.c | 175 +++++++++++++++++----------------- mps/code/protxc.h | 2 +- mps/code/th.h | 6 +- mps/code/thxc.c | 36 ++++--- 7 files changed, 353 insertions(+), 111 deletions(-) create mode 100644 mps/code/forktest.c diff --git a/mps/code/forktest.c b/mps/code/forktest.c new file mode 100644 index 00000000000..c078bff657e --- /dev/null +++ b/mps/code/forktest.c @@ -0,0 +1,228 @@ +/* tagtest.c: TAGGED POINTER TEST + * + * $Id: //info.ravenbrook.com/project/mps/branch/2018-06-13/fork/code/tagtest.c#1 $ + * Copyright (c) 2018 Ravenbrook Limited. See end of file for license. + * + * .overview: This test case is a regression test for job004062. It + * checks that the MPS correctly runs in the child process after a + * fork() on FreeBSD, Linux or OS X. + */ + +#include +#include +#include + +#include "mps.h" +#include "mpsavm.h" +#include "mpscamc.h" +#include "testlib.h" + +enum { + TYPE_REF, + TYPE_FWD, + TYPE_PAD +}; + +typedef struct obj_s { + unsigned type; /* One of the TYPE_ enums */ + union { + struct obj_s *ref; + mps_addr_t fwd; + size_t pad; + } u; +} obj_s, *obj_t; + +#define ALIGNMENT sizeof(obj_s) + +/* Align up a to a multiple of b. */ +#define ALIGN_UP(a, b) (((a) + (b) - 1) & ~((b) - 1)) + +static void obj_fwd(mps_addr_t old, mps_addr_t new) +{ + obj_t obj = old; + obj->type = TYPE_FWD; + obj->u.fwd = new; +} + +static mps_addr_t obj_isfwd(mps_addr_t addr) +{ + obj_t obj = addr; + if (obj->type == TYPE_FWD) { + return obj->u.fwd; + } else { + return NULL; + } +} + +static void obj_pad(mps_addr_t addr, size_t size) +{ + obj_t obj = addr; + obj->type = TYPE_PAD; + obj->u.pad = size; +} + +static mps_addr_t obj_skip(mps_addr_t addr) +{ + obj_t obj = addr; + size_t size; + if (obj->type == TYPE_PAD) { + size = obj->u.pad; + } else { + size = sizeof(obj_s); + } + return (char *)addr + ALIGN_UP(size, ALIGNMENT); +} + +static mps_res_t obj_scan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) +{ + MPS_SCAN_BEGIN(ss) { + while (base < limit) { + obj_t obj = base; + if (obj->type == TYPE_REF) { + mps_addr_t p = obj->u.ref; + mps_res_t res = MPS_FIX12(ss, &p); + if (res != MPS_RES_OK) { + return res; + } + obj->u.ref = p; + } + base = obj_skip(base); + } + } MPS_SCAN_END(ss); + return MPS_RES_OK; +} + +int main(int argc, char *argv[]) +{ + void *marker = ▮ + int pid; + mps_arena_t arena; + mps_fmt_t obj_fmt; + mps_pool_t pool; + mps_thr_t thread; + mps_root_t stack_root; + mps_ap_t obj_ap; + size_t i; + obj_t obj, first; + + testlib_init(argc, argv); + + /* Set the pause time to be very small so that the incremental + collector has to leave a read barrier in place for us to hit. */ + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_PAUSE_TIME, 0.0); + die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), + "mps_arena_create"); + } MPS_ARGS_END(args); + mps_arena_park(arena); + + die(mps_thread_reg(&thread, arena), "Couldn't register thread"); + die(mps_root_create_thread(&stack_root, arena, thread, marker), + "Couldn't create thread root"); + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FMT_ALIGN, ALIGNMENT); + MPS_ARGS_ADD(args, MPS_KEY_FMT_SCAN, obj_scan); + MPS_ARGS_ADD(args, MPS_KEY_FMT_SKIP, obj_skip); + MPS_ARGS_ADD(args, MPS_KEY_FMT_FWD, obj_fwd); + MPS_ARGS_ADD(args, MPS_KEY_FMT_ISFWD, obj_isfwd); + MPS_ARGS_ADD(args, MPS_KEY_FMT_PAD, obj_pad); + die(mps_fmt_create_k(&obj_fmt, arena, args), "Couldn't create obj format"); + } MPS_ARGS_END(args); + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FORMAT, obj_fmt); + die(mps_pool_create_k(&pool, arena, mps_class_amc(), args), + "Couldn't create pool"); + } MPS_ARGS_END(args); + + die(mps_ap_create_k(&obj_ap, pool, mps_args_none), + "Couldn't create obj allocation point"); + + /* Create a linked list of a million cells. */ + first = NULL; + for (i = 0; i < 100000; ++i) { + size_t size = ALIGN_UP(sizeof(obj_s), ALIGNMENT); + mps_addr_t addr; + do { + die(mps_reserve(&addr, obj_ap, size), "Couldn't allocate."); + obj = addr; + obj->type = TYPE_REF; + obj->u.ref = NULL; + } while (!mps_commit(obj_ap, addr, size)); + obj->u.ref = first; + first = obj; + } + + pid = fork(); + cdie(pid >= 0, "fork failed"); + if (pid == 0) { + /* Child: allow a collection to start */ + mps_arena_release(arena); + + /* Read a bunch of stuff so that we hit the read barrier. */ + for (obj = first; obj != NULL; obj = obj->u.ref) { + Insist(obj->type == TYPE_REF); + } + + mps_arena_park(arena); + } else { + /* Parent: wait for child result */ + int stat; + cdie(pid == waitpid(pid, &stat, 0), "waitpid failed"); + cdie(WIFEXITED(stat), "child did not exit normally"); + cdie(WEXITSTATUS(stat) == 0, "child exited with nonzero status"); + printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); + } + + mps_ap_destroy(obj_ap); + mps_pool_destroy(pool); + mps_fmt_destroy(obj_fmt); + mps_root_destroy(stack_root); + mps_thread_dereg(thread); + mps_arena_destroy(arena); + + return 0; +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (c) 2018 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/mps/code/global.c b/mps/code/global.c index a77a76d5622..accd382935f 100644 --- a/mps/code/global.c +++ b/mps/code/global.c @@ -100,13 +100,17 @@ static void arenaDenounce(Arena arena) } +/* GlobalsArenaMap -- map a function over the arenas. The caller must + * have acquired the ring lock. */ -/* GlobalsArenaRing -- return the global ring of arenas */ - -Ring GlobalsArenaRing(void) +void GlobalsArenaMap(void (*func)(Arena arena, void *closure), void *closure) { - AVER(arenaRingInit); - return &arenaRing; + Ring node, nextNode; + RING_FOR(node, &arenaRing, nextNode) { + Globals arenaGlobals = RING_ELT(Globals, globalRing, node); + Arena arena = GlobalsArena(arenaGlobals); + func(arena, closure); + } } @@ -601,7 +605,6 @@ Bool ArenaAccess(Addr addr, AccessSet mode, MutatorContext context) Res res; arenaClaimRingLock(); /* */ - AVERT(Ring, &arenaRing); RING_FOR(node, &arenaRing, nextNode) { Globals arenaGlobals = RING_ELT(Globals, globalRing, node); diff --git a/mps/code/mpm.h b/mps/code/mpm.h index be6d949f0c7..dac4a9ace68 100644 --- a/mps/code/mpm.h +++ b/mps/code/mpm.h @@ -492,7 +492,7 @@ extern Res GlobalsCompleteCreate(Globals arenaGlobals); extern void GlobalsPrepareToDestroy(Globals arenaGlobals); extern Res GlobalsDescribe(Globals arena, mps_lib_FILE *stream, Count depth); extern Ring GlobalsRememberedSummaryRing(Globals); -extern Ring GlobalsArenaRing(void); +extern void GlobalsArenaMap(void (*func)(Arena arena, void *closure), void *closure); #define ArenaGlobals(arena) (&(arena)->globals) #define GlobalsArena(glob) PARENT(ArenaStruct, globals, glob) diff --git a/mps/code/protxc.c b/mps/code/protxc.c index 674e2c70180..aa7a359a3d4 100644 --- a/mps/code/protxc.c +++ b/mps/code/protxc.c @@ -286,9 +286,20 @@ static void *protCatchThread(void *p) { } +/* protSetupThread -- the Mach port number of either the very first + * thread to create an arena, or else the child thread after a fork. + * + * The purpose is to avoid registering this thread twice if the client + * program calls mps_thread_reg on it. We need this special case + * because we don't require thread registration of the sole thread of + * a single-threaded mutator. + */ +static mach_port_t protSetupThread = MACH_PORT_NULL; + + /* ProtThreadRegister -- register a thread for protection exception handling */ -extern void ProtThreadRegister(Bool setup) +extern void ProtThreadRegister(void) { kern_return_t kr; mach_msg_type_number_t old_exception_count = 1; @@ -298,23 +309,13 @@ extern void ProtThreadRegister(Bool setup) exception_behavior_t old_behaviors; thread_state_flavor_t old_flavors; mach_port_t self; - static mach_port_t setupThread = MACH_PORT_NULL; self = mach_thread_self(); AVER(MACH_PORT_VALID(self)); - /* Avoid setting up the exception handler for the thread that calls - ProtSetup twice, in the case where the mutator registers that thread - explicitly. We need a special case because we don't require thread - registration of the sole thread of a single-threaded mutator. */ - if (setup) { - AVER(setupThread == MACH_PORT_NULL); - setupThread = self; - } else { - AVER(setupThread != MACH_PORT_NULL); - if (self == setupThread) - return; - } + /* Avoid setting up the exception handler twice. */ + if (self == protSetupThread) + return; /* Ask to receive EXC_BAD_ACCESS exceptions on our port, complete with thread state and identity information in the message. @@ -342,77 +343,17 @@ extern void ProtThreadRegister(Bool setup) } -/* atfork handlers -- support for fork() - * - * In order to support fork(), we need to solve the following problems: - * - * (1) the MPS lock might be held by another thread; - * - * (2.1) only the thread that called fork() exists in the child process; - * - * (2.2) in particular, the protection fault handling thread does not - * exist in the child process. - * - * (3) this thread has a new Mach port number in the child. - * - * TODO: what about protExcPort? +/* protExcThreadStart -- create exception port, register the current + * thread with that port, and create a thread to handle exception + * messages. */ -static void prot_atfork_prepare(void) -{ - Ring node, nextNode; - - /* Take all the locks, solving (1). */ - - /* For each arena, remember which thread is the current thread (if - any), solving (2.1). */ - RING_FOR(node, GlobalsArenaRing(), nextNode) { - Globals arenaGlobals = RING_ELT(Globals, globalRing, node); - Arena arena = GlobalsArena(arenaGlobals); - ThreadRingForkPrepare(ArenaThreadRing(arena), ArenaDeadRing(arena)); - } -} - -static void prot_atfork_parent(void) -{ - Ring node, nextNode; - /* Release all the locks in reverse order. */ - - /* For each arena, mark threads as not forking any more. */ - RING_FOR(node, GlobalsArenaRing(), nextNode) { - Globals arenaGlobals = RING_ELT(Globals, globalRing, node); - Arena arena = GlobalsArena(arenaGlobals); - ThreadRingForkParent(ArenaThreadRing(arena), ArenaDeadRing(arena)); - } -} - -static void prot_atfork_child(void) -{ - Ring node, nextNode; - /* For each arena, move all the threads to the dead ring, solving - (2.1), except for the thread that was marked as current by the - prepare handler, for which we update its mach port number, - solving (3). */ - RING_FOR(node, GlobalsArenaRing(), nextNode) { - Globals arenaGlobals = RING_ELT(Globals, globalRing, node); - Arena arena = GlobalsArena(arenaGlobals); - ThreadRingForkChild(ArenaThreadRing(arena), ArenaDeadRing(arena)); - } - - /* Restart the protection fault handling thread, solving (2.2). */ - - /* Release all the locks in reverse order, solving (1). */ -} - - -/* ProtSetup -- set up protection exception handling */ - -static void protSetupInner(void) +static void protExcThreadStart(void) { kern_return_t kr; - int pr; - pthread_t excThread; mach_port_t self; + pthread_t excThread; + int pr; /* Create a port to send and receive exceptions. */ self = mach_task_self(); @@ -436,19 +377,81 @@ static void protSetupInner(void) if (kr != KERN_SUCCESS) mach_error("ERROR: MPS mach_port_insert_right", kr); /* .trans.must */ - ProtThreadRegister(TRUE); + protSetupThread = MACH_PORT_NULL; + ProtThreadRegister(); + protSetupThread = self; - /* Launch the exception handling thread. We use pthread_create because - it's much simpler than setting up a thread from scratch using Mach, - and that's basically what it does. See [Libc] - */ + /* Launch the exception handling thread. We use pthread_create + * because it's much simpler than setting up a thread from scratch + * using Mach, and that's basically what it does. See [Libc] + * */ pr = pthread_create(&excThread, NULL, protCatchThread, NULL); AVER(pr == 0); if (pr != 0) fprintf(stderr, "ERROR: MPS pthread_create: %d\n", pr); /* .trans.must */ +} + + +/* atfork handlers -- support for fork() + * + * In order to support fork(), we need to solve the following problems: + * + * .fork.lock: the MPS lock might be held by another thread; + * + * .fork.threads: only the thread that called fork() exists in the + * child process; + * + * .fork.exc-thread: in particular, the exception handling thread does + * not exist in the child process. Also, the port on which the + * exception thread receives its messages apparently does not exist in + * the child and so needs to be re-allocated. + * + * .fork.mach-port: the thread that does survive in the child process + * has a different Mach port number in the child. + */ + +static void protAtForkPrepare(void) +{ + /* Take all the locks (see .fork.lock). */ + + /* For each arena, remember which thread is the current thread, if + any (see .fork.threads). */ + GlobalsArenaMap(ThreadRingForkPrepare, UNUSED_POINTER); +} + +static void protAtForkParent(void) +{ + /* Release all the locks in reverse order (see .fork.lock). */ + + /* For each arena, mark threads as not forking any more + (see .fork.threads). */ + GlobalsArenaMap(ThreadRingForkParent, UNUSED_POINTER); +} + +static void protAtForkChild(void) +{ + /* For each arena, move all threads to the dead ring, except for the + thread that was marked as current by the prepare handler (see + .fork.threads), for which we update its mach port number (see + .fork.mach-port). */ + GlobalsArenaMap(ThreadRingForkChild, UNUSED_POINTER); + + /* Restart the exception handling thread (see .fork.exc-thread). */ + protExcThreadStart(); + + /* Release all the locks in reverse order, solving (see .fork.lock). */ +} + + +/* ProtSetup -- set up protection exception handling */ + +static void protSetupInner(void) +{ + AVER(protSetupThread == MACH_PORT_NULL); + protExcThreadStart(); /* Install fork handlers. */ - pthread_atfork(prot_atfork_prepare, prot_atfork_parent, prot_atfork_child); + pthread_atfork(protAtForkPrepare, protAtForkParent, protAtForkChild); } void ProtSetup(void) diff --git a/mps/code/protxc.h b/mps/code/protxc.h index 26403b47cbc..1e674a3cb20 100644 --- a/mps/code/protxc.h +++ b/mps/code/protxc.h @@ -7,7 +7,7 @@ #ifndef protxc_h #define protxc_h -extern void ProtThreadRegister(Bool setup); +extern void ProtThreadRegister(void); #endif /* protxc_h */ diff --git a/mps/code/th.h b/mps/code/th.h index efd6b53e040..b5a8b67ba5b 100644 --- a/mps/code/th.h +++ b/mps/code/th.h @@ -75,9 +75,9 @@ extern Res ThreadScan(ScanState ss, Thread thread, Word *stackCold, /* ThreadRingFork* -- atfork handlers for Unix */ -void ThreadRingForkPrepare(Ring threadRing, Ring deadRing); -void ThreadRingForkParent(Ring threadRing, Ring deadRing); -void ThreadRingForkChild(Ring threadRing, Ring deadRing); +void ThreadRingForkPrepare(Arena arena, void *closure); +void ThreadRingForkParent(Arena arena, void *closure); +void ThreadRingForkChild(Arena arena, void *closure); #endif /* th_h */ diff --git a/mps/code/thxc.c b/mps/code/thxc.c index bf074b6ae99..deaff0e2001 100644 --- a/mps/code/thxc.c +++ b/mps/code/thxc.c @@ -87,7 +87,7 @@ Res ThreadRegister(Thread *threadReturn, Arena arena) thread->sig = ThreadSig; AVERT(Thread, thread); - ProtThreadRegister(FALSE); + ProtThreadRegister(); ring = ArenaThreadRing(arena); @@ -115,7 +115,7 @@ void ThreadDeregister(Thread thread, Arena arena) /* mapThreadRing -- map over threads on ring calling a function on - * each one except the current thread. + * each one. * * Threads that are found to be dead (that is, if func returns FALSE) * are marked as dead and moved to deadRing, in order to implement @@ -125,21 +125,16 @@ void ThreadDeregister(Thread thread, Arena arena) static void mapThreadRing(Ring threadRing, Ring deadRing, Bool (*func)(Thread)) { Ring node, next; - mach_port_t self; AVERT(Ring, threadRing); AVERT(Ring, deadRing); AVER(FUNCHECK(func)); - self = mach_thread_self(); - AVER(MACH_PORT_VALID(self)); RING_FOR(node, threadRing, next) { Thread thread = RING_ELT(Thread, arenaRing, node); AVERT(Thread, thread); AVER(thread->alive); - if (thread->port != self - && !(*func)(thread)) - { + if (!(*func)(thread)) { thread->alive = FALSE; RingRemove(&thread->arenaRing); RingAppend(deadRing, &thread->arenaRing); @@ -151,6 +146,11 @@ static void mapThreadRing(Ring threadRing, Ring deadRing, Bool (*func)(Thread)) static Bool threadSuspend(Thread thread) { kern_return_t kern_return; + mach_port_t self = mach_thread_self(); + AVER(MACH_PORT_VALID(self)); + if (thread->port == self) + return TRUE; + kern_return = thread_suspend(thread->port); /* No rendezvous is necessary: thread_suspend "prevents the thread * from executing any more user-level instructions" */ @@ -174,6 +174,11 @@ void ThreadRingSuspend(Ring threadRing, Ring deadRing) static Bool threadResume(Thread thread) { kern_return_t kern_return; + mach_port_t self = mach_thread_self(); + AVER(MACH_PORT_VALID(self)); + if (thread->port == self) + return TRUE; + kern_return = thread_resume(thread->port); /* Mach has no equivalent of EAGAIN. */ AVER(kern_return == KERN_SUCCESS); @@ -202,9 +207,10 @@ static Bool threadForkPrepare(Thread thread) /* ThreadRingForkPrepare -- prepare for a fork by marking the current * thread as forking. */ -void ThreadRingForkPrepare(Ring threadRing, Ring deadRing) +void ThreadRingForkPrepare(Arena arena, void *closure) { - mapThreadRing(threadRing, deadRing, threadForkPrepare); + AVER(closure == UNUSED_POINTER); + mapThreadRing(ArenaThreadRing(arena), ArenaDeadRing(arena), threadForkPrepare); } @@ -217,9 +223,10 @@ static Bool threadForkParent(Thread thread) /* ThreadRingForkParent -- clear the forking flag in the parent after * a fork. */ -void ThreadRingForkParent(Ring threadRing, Ring deadRing) +void ThreadRingForkParent(Arena arena, void *closure) { - mapThreadRing(threadRing, deadRing, threadForkParent); + AVER(closure == UNUSED_POINTER); + mapThreadRing(ArenaThreadRing(arena), ArenaDeadRing(arena), threadForkParent); } @@ -238,9 +245,10 @@ static Bool threadForkChild(Thread thread) /* ThreadRingForkChild -- update the mach thread port for the current * thread; move all other threads to the dead ring. */ -void ThreadRingForkChild(Ring threadRing, Ring deadRing) +void ThreadRingForkChild(Arena arena, void *closure) { - mapThreadRing(threadRing, deadRing, threadForkChild); + AVER(closure == UNUSED_POINTER); + mapThreadRing(ArenaThreadRing(arena), ArenaDeadRing(arena), threadForkChild); } From e70b4c273c06490005413a1145520a5c75102678 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 14 Jun 2018 16:25:08 +0100 Subject: [PATCH 12/27] Take locks before fork(), release them in the parent and reinitialize them in the child. Add forktest target to the Xcode project. Write design for fork safety and cross-reference from the code. Update release notes. Copied from Perforce Change: 193760 --- mps/code/commpost.nmk | 3 - mps/code/commpre.nmk | 1 - mps/code/global.c | 43 +++++++++- mps/code/lock.h | 5 ++ mps/code/lockan.c | 7 ++ mps/code/lockix.c | 6 +- mps/code/lockw3.c | 14 +++- mps/code/mpm.h | 5 +- mps/code/mps.xcodeproj/project.pbxproj | 110 ++++++++++++++++++++++++- mps/code/protxc.c | 52 +++++------- mps/code/th.h | 6 +- mps/code/thxc.c | 19 +++-- mps/design/lock.txt | 9 ++ mps/design/thread-safety.txt | 73 ++++++++++++++++ mps/manual/source/release.rst | 12 +++ 15 files changed, 307 insertions(+), 58 deletions(-) diff --git a/mps/code/commpost.nmk b/mps/code/commpost.nmk index 97f6733feb3..2e5a999744c 100644 --- a/mps/code/commpost.nmk +++ b/mps/code/commpost.nmk @@ -237,9 +237,6 @@ $(PFM)\$(VARIETY)\finalcv.exe: $(PFM)\$(VARIETY)\finalcv.obj \ $(PFM)\$(VARIETY)\finaltest.exe: $(PFM)\$(VARIETY)\finaltest.obj \ $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) -$(PFM)\$(VARIETY)\forktest.exe: $(PFM)\$(VARIETY)\forktest.obj \ - $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) - $(PFM)\$(VARIETY)\fotest.exe: $(PFM)\$(VARIETY)\fotest.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) diff --git a/mps/code/commpre.nmk b/mps/code/commpre.nmk index b3007b86bb8..8a79096f42c 100644 --- a/mps/code/commpre.nmk +++ b/mps/code/commpre.nmk @@ -77,7 +77,6 @@ TEST_TARGETS=\ expt825.exe \ finalcv.exe \ finaltest.exe \ - forktest.exe \ fotest.exe \ gcbench.exe \ landtest.exe \ diff --git a/mps/code/global.c b/mps/code/global.c index accd382935f..73b7c2fb5df 100644 --- a/mps/code/global.c +++ b/mps/code/global.c @@ -53,6 +53,45 @@ static void arenaReleaseRingLock(void) } +/* GlobalsClaimAll -- claim all MPS locks */ + +void GlobalsClaimAll(void) +{ + LockClaimGlobalRecursive(); + arenaClaimRingLock(); + GlobalsArenaMap(ArenaEnter); +} + +/* GlobalsReleaseAll -- release all MPS locks. GlobalsClaimAll must + * previously have been called. */ + +void GlobalsReleaseAll(void) +{ + GlobalsArenaMap(ArenaLeave); + arenaReleaseRingLock(); + LockReleaseGlobalRecursive(); +} + +/* arenaReinitLock -- reinitialize the lock for an arena */ + +static void arenaReinitLock(Arena arena) +{ + AVERT(Arena, arena); + ShieldLeave(arena); + LockInit(ArenaGlobals(arena)->lock); +} + +/* GlobalsReinitializeAll -- reinitialize all MPS locks, and leave the + * shield for all arenas. GlobalsClaimAll must previously have been + * called. */ + +void GlobalsReinitializeAll(void) +{ + LockInitGlobal(); + GlobalsArenaMap(arenaReinitLock); +} + + /* arenaAnnounce -- add a new arena into the global ring of arenas * * On entry, the arena must not be locked (there should be no need, @@ -103,13 +142,13 @@ static void arenaDenounce(Arena arena) /* GlobalsArenaMap -- map a function over the arenas. The caller must * have acquired the ring lock. */ -void GlobalsArenaMap(void (*func)(Arena arena, void *closure), void *closure) +void GlobalsArenaMap(void (*func)(Arena arena)) { Ring node, nextNode; RING_FOR(node, &arenaRing, nextNode) { Globals arenaGlobals = RING_ELT(Globals, globalRing, node); Arena arena = GlobalsArena(arenaGlobals); - func(arena, closure); + func(arena); } } diff --git a/mps/code/lock.h b/mps/code/lock.h index 1a02d7997ed..9c3b9c080d0 100644 --- a/mps/code/lock.h +++ b/mps/code/lock.h @@ -21,6 +21,11 @@ extern size_t LockSize(void); +/* LockInitGlobal -- initialize global locks */ + +extern void LockInitGlobal(void); + + /* LockInit/Finish * * lock points to the allocated lock structure. A lock has no diff --git a/mps/code/lockan.c b/mps/code/lockan.c index 8b1b61a5243..1c75024fda4 100644 --- a/mps/code/lockan.c +++ b/mps/code/lockan.c @@ -106,6 +106,13 @@ static Lock globalLock = &globalLockStruct; static Lock globalRecLock = &globalRecursiveLockStruct; +void LockInitGlobal(void) +{ + globalLock->claims = 0; + LockInit(globalLock); + globalRecursiveLock->claims = 0; + LockInit(globalRecursiveLock); +} void (LockClaimGlobalRecursive)(void) { diff --git a/mps/code/lockix.c b/mps/code/lockix.c index 4c42d754ba2..2b80a165456 100644 --- a/mps/code/lockix.c +++ b/mps/code/lockix.c @@ -212,7 +212,7 @@ static Lock globalLock = &globalLockStruct; static Lock globalRecLock = &globalRecLockStruct; static pthread_once_t isGlobalLockInit = PTHREAD_ONCE_INIT; -static void globalLockInit(void) +void LockInitGlobal(void) { LockInit(globalLock); LockInit(globalRecLock); @@ -226,7 +226,7 @@ void (LockClaimGlobalRecursive)(void) int res; /* Ensure the global lock has been initialized */ - res = pthread_once(&isGlobalLockInit, globalLockInit); + res = pthread_once(&isGlobalLockInit, LockInitGlobal); AVER(res == 0); LockClaimRecursive(globalRecLock); } @@ -247,7 +247,7 @@ void (LockClaimGlobal)(void) int res; /* Ensure the global lock has been initialized */ - res = pthread_once(&isGlobalLockInit, globalLockInit); + res = pthread_once(&isGlobalLockInit, LockInitGlobal); AVER(res == 0); LockClaim(globalLock); } diff --git a/mps/code/lockw3.c b/mps/code/lockw3.c index a471687514e..cbd38e4ed7e 100644 --- a/mps/code/lockw3.c +++ b/mps/code/lockw3.c @@ -127,15 +127,21 @@ static Lock globalLock = &globalLockStruct; static Lock globalRecLock = &globalRecLockStruct; static Bool globalLockInit = FALSE; /* TRUE iff initialized */ +void LockInitGlobal(void) +{ + globalLock->claims = 0; + LockInit(globalLock); + globalRecLock->claims = 0; + LockInit(globalRecLock); + globalLockInit = TRUE; +} static void lockEnsureGlobalLock(void) { /* Ensure both global locks have been initialized. */ - /* There is a race condition initializing them. */ + /* There is a race condition initializing them (job004056). */ if (!globalLockInit) { - LockInit(globalLock); - LockInit(globalRecLock); - globalLockInit = TRUE; + LockInitGlobal(); } } diff --git a/mps/code/mpm.h b/mps/code/mpm.h index dac4a9ace68..410dd0b3c6c 100644 --- a/mps/code/mpm.h +++ b/mps/code/mpm.h @@ -492,7 +492,10 @@ extern Res GlobalsCompleteCreate(Globals arenaGlobals); extern void GlobalsPrepareToDestroy(Globals arenaGlobals); extern Res GlobalsDescribe(Globals arena, mps_lib_FILE *stream, Count depth); extern Ring GlobalsRememberedSummaryRing(Globals); -extern void GlobalsArenaMap(void (*func)(Arena arena, void *closure), void *closure); +extern void GlobalsArenaMap(void (*func)(Arena arena)); +extern void GlobalsClaimAll(void); +extern void GlobalsReleaseAll(void); +extern void GlobalsReinitializeAll(void); #define ArenaGlobals(arena) (&(arena)->globals) #define GlobalsArena(glob) PARENT(ArenaStruct, globals, glob) diff --git a/mps/code/mps.xcodeproj/project.pbxproj b/mps/code/mps.xcodeproj/project.pbxproj index 5b34d8a3584..05c0f078106 100644 --- a/mps/code/mps.xcodeproj/project.pbxproj +++ b/mps/code/mps.xcodeproj/project.pbxproj @@ -93,6 +93,7 @@ 2291A5E8175CB20E001D4920 /* PBXTargetDependency */, 3114A5CC156E932C001E0AA3 /* PBXTargetDependency */, 3114A5EA156E93C4001E0AA3 /* PBXTargetDependency */, + 22EA3F4820D2B23F0065F5B6 /* PBXTargetDependency */, 224CC79D175E187C002FF81B /* PBXTargetDependency */, 22B2BC3F18B643B700C33E63 /* PBXTargetDependency */, 3114A65B156E95B4001E0AA3 /* PBXTargetDependency */, @@ -172,6 +173,9 @@ 22C2ACA718BE400A006B3677 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; 22C2ACA918BE400A006B3677 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; 22C2ACB018BE4049006B3677 /* nailboardtest.c in Sources */ = {isa = PBXBuildFile; fileRef = 22C2ACA018BE3FEC006B3677 /* nailboardtest.c */; }; + 22EA3F3D20D2B0D90065F5B6 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; + 22EA3F3F20D2B0D90065F5B6 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; + 22EA3F4620D2B0FD0065F5B6 /* forktest.c in Sources */ = {isa = PBXBuildFile; fileRef = 22EA3F3720D2B0730065F5B6 /* forktest.c */; }; 22F846B518F437B900982BA7 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; 22F846B718F437B900982BA7 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; 22F846BE18F437D700982BA7 /* lockut.c in Sources */ = {isa = PBXBuildFile; fileRef = 22F846AF18F4379C00982BA7 /* lockut.c */; }; @@ -531,6 +535,20 @@ remoteGlobalIDString = 3104AFF1156D37A0000A585A; remoteInfo = all; }; + 22EA3F3A20D2B0D90065F5B6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 31EEABFA156AAF9D00714D05; + remoteInfo = mps; + }; + 22EA3F4720D2B23F0065F5B6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 22EA3F3820D2B0D90065F5B6; + remoteInfo = forktest; + }; 22F846B218F437B900982BA7 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; @@ -1098,6 +1116,15 @@ ); runOnlyForDeploymentPostprocessing = 1; }; + 22EA3F4020D2B0D90065F5B6 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; 22F846B818F437B900982BA7 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -1491,6 +1518,8 @@ 22DD93E218ED815F00240DD2 /* land.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = land.txt; path = ../design/land.txt; sourceTree = ""; }; 22E30E821886FF1400D98EA9 /* nailboard.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nailboard.c; sourceTree = ""; }; 22E30E831886FF1400D98EA9 /* nailboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nailboard.h; sourceTree = ""; }; + 22EA3F3720D2B0730065F5B6 /* forktest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = forktest.c; sourceTree = ""; }; + 22EA3F4520D2B0D90065F5B6 /* forktest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = forktest; sourceTree = BUILT_PRODUCTS_DIR; }; 22F846AF18F4379C00982BA7 /* lockut.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lockut.c; sourceTree = ""; }; 22F846BD18F437B900982BA7 /* lockut */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = lockut; sourceTree = BUILT_PRODUCTS_DIR; }; 22FA177516E8D6FC0098B23F /* amcssth */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = amcssth; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1569,6 +1598,7 @@ 31160D971899540D0071EB17 /* buffer.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = buffer.txt; path = ../design/buffer.txt; sourceTree = ""; }; 31160D981899540D0071EB17 /* cbs.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = cbs.txt; path = ../design/cbs.txt; sourceTree = ""; }; 31160D991899540D0071EB17 /* check.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = check.txt; path = ../design/check.txt; sourceTree = ""; }; + 31160D9A1899540D0071EB17 /* pool.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = pool.txt; path = ../design/pool.txt; sourceTree = ""; }; 31160D9B1899540D0071EB17 /* collection.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = collection.txt; path = ../design/collection.txt; sourceTree = ""; }; 31160D9C1899540D0071EB17 /* config.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = config.txt; path = ../design/config.txt; sourceTree = ""; }; 31160D9D1899540D0071EB17 /* critical-path.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "critical-path.txt"; path = "../design/critical-path.txt"; sourceTree = ""; }; @@ -1588,7 +1618,6 @@ 31160DAB1899540D0071EB17 /* message-gc.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "message-gc.txt"; path = "../design/message-gc.txt"; sourceTree = ""; }; 31160DAC1899540D0071EB17 /* message.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = message.txt; path = ../design/message.txt; sourceTree = ""; }; 31160DAD1899540D0071EB17 /* object-debug.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "object-debug.txt"; path = "../design/object-debug.txt"; sourceTree = ""; }; - 31160D9A1899540D0071EB17 /* pool.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "pool.txt"; path = "../design/pool.txt"; sourceTree = ""; }; 31160DAF1899540D0071EB17 /* poolamc.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = poolamc.txt; path = ../design/poolamc.txt; sourceTree = ""; }; 31160DB01899540D0071EB17 /* poolams.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = poolams.txt; path = ../design/poolams.txt; sourceTree = ""; }; 31160DB11899540D0071EB17 /* poolawl.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = poolawl.txt; path = ../design/poolawl.txt; sourceTree = ""; }; @@ -1852,6 +1881,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 22EA3F3E20D2B0D90065F5B6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 22EA3F3F20D2B0D90065F5B6 /* libmps.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 22F846B618F437B900982BA7 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -2324,6 +2361,7 @@ 22FACED5188807FF000FDBC1 /* fmtno.h */, 22FACED6188807FF000FDBC1 /* fmtscheme.c */, 22FACED7188807FF000FDBC1 /* fmtscheme.h */, + 22EA3F3720D2B0730065F5B6 /* forktest.c */, 224CC79E175E3202002FF81B /* fotest.c */, 2291A5E9175CB4EC001D4920 /* landtest.c */, 2231BB6818CA9834002D6322 /* locbwcss.c */, @@ -2442,6 +2480,7 @@ 22F846BD18F437B900982BA7 /* lockut */, 31108A471C6B90E900E728EA /* tagtest */, 223E796519EAB00B00DC26A6 /* sncss */, + 22EA3F4520D2B0D90065F5B6 /* forktest */, ); name = Products; sourceTree = ""; @@ -2789,6 +2828,24 @@ productReference = 22C2ACAF18BE400A006B3677 /* nailboardtest */; productType = "com.apple.product-type.tool"; }; + 22EA3F3820D2B0D90065F5B6 /* forktest */ = { + isa = PBXNativeTarget; + buildConfigurationList = 22EA3F4120D2B0D90065F5B6 /* Build configuration list for PBXNativeTarget "forktest" */; + buildPhases = ( + 22EA3F3B20D2B0D90065F5B6 /* Sources */, + 22EA3F3E20D2B0D90065F5B6 /* Frameworks */, + 22EA3F4020D2B0D90065F5B6 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + 22EA3F3920D2B0D90065F5B6 /* PBXTargetDependency */, + ); + name = forktest; + productName = mv2test; + productReference = 22EA3F4520D2B0D90065F5B6 /* forktest */; + productType = "com.apple.product-type.tool"; + }; 22F846B018F437B900982BA7 /* lockut */ = { isa = PBXNativeTarget; buildConfigurationList = 22F846B918F437B900982BA7 /* Build configuration list for PBXNativeTarget "lockut" */; @@ -3549,6 +3606,7 @@ 2291A5C1175CAFCA001D4920 /* expt825 */, 3114A5BC156E9315001E0AA3 /* finalcv */, 3114A5D5156E93A0001E0AA3 /* finaltest */, + 22EA3F3820D2B0D90065F5B6 /* forktest */, 224CC78C175E1821002FF81B /* fotest */, 6313D46718A400B200EB03EF /* gcbench */, 3114A64B156E9596001E0AA3 /* landtest */, @@ -3748,6 +3806,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 22EA3F3B20D2B0D90065F5B6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 22EA3F4620D2B0FD0065F5B6 /* forktest.c in Sources */, + 22EA3F3D20D2B0D90065F5B6 /* testlib.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 22F846B318F437B900982BA7 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -4303,6 +4370,16 @@ target = 3104AFF1156D37A0000A585A /* all */; targetProxy = 22CDE92D16E9EB9300366D0A /* PBXContainerItemProxy */; }; + 22EA3F3920D2B0D90065F5B6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 31EEABFA156AAF9D00714D05 /* mps */; + targetProxy = 22EA3F3A20D2B0D90065F5B6 /* PBXContainerItemProxy */; + }; + 22EA3F4820D2B23F0065F5B6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 22EA3F3820D2B0D90065F5B6 /* forktest */; + targetProxy = 22EA3F4720D2B23F0065F5B6 /* PBXContainerItemProxy */; + }; 22F846B118F437B900982BA7 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 31EEABFA156AAF9D00714D05 /* mps */; @@ -4917,6 +4994,27 @@ }; name = Release; }; + 22EA3F4220D2B0D90065F5B6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 22EA3F4320D2B0D90065F5B6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 22EA3F4420D2B0D90065F5B6 /* RASH */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = RASH; + }; 22F846BA18F437B900982BA7 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -6133,6 +6231,16 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 22EA3F4120D2B0D90065F5B6 /* Build configuration list for PBXNativeTarget "forktest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 22EA3F4220D2B0D90065F5B6 /* Debug */, + 22EA3F4320D2B0D90065F5B6 /* Release */, + 22EA3F4420D2B0D90065F5B6 /* RASH */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 22F846B918F437B900982BA7 /* Build configuration list for PBXNativeTarget "lockut" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/mps/code/protxc.c b/mps/code/protxc.c index aa7a359a3d4..4792fcdb4b2 100644 --- a/mps/code/protxc.c +++ b/mps/code/protxc.c @@ -392,54 +392,44 @@ static void protExcThreadStart(void) } -/* atfork handlers -- support for fork() - * - * In order to support fork(), we need to solve the following problems: - * - * .fork.lock: the MPS lock might be held by another thread; - * - * .fork.threads: only the thread that called fork() exists in the - * child process; - * - * .fork.exc-thread: in particular, the exception handling thread does - * not exist in the child process. Also, the port on which the - * exception thread receives its messages apparently does not exist in - * the child and so needs to be re-allocated. - * - * .fork.mach-port: the thread that does survive in the child process - * has a different Mach port number in the child. - */ +/* atfork handlers -- support for fork(). See */ static void protAtForkPrepare(void) { - /* Take all the locks (see .fork.lock). */ + /* Take all the locks . */ + GlobalsClaimAll(); /* For each arena, remember which thread is the current thread, if - any (see .fork.threads). */ - GlobalsArenaMap(ThreadRingForkPrepare, UNUSED_POINTER); + any . */ + GlobalsArenaMap(ThreadRingForkPrepare); } static void protAtForkParent(void) { - /* Release all the locks in reverse order (see .fork.lock). */ - /* For each arena, mark threads as not forking any more - (see .fork.threads). */ - GlobalsArenaMap(ThreadRingForkParent, UNUSED_POINTER); + . */ + GlobalsArenaMap(ThreadRingForkParent); + + /* Release all the locks in reverse order + . */ + GlobalsReleaseAll(); } static void protAtForkChild(void) { /* For each arena, move all threads to the dead ring, except for the - thread that was marked as current by the prepare handler (see - .fork.threads), for which we update its mach port number (see - .fork.mach-port). */ - GlobalsArenaMap(ThreadRingForkChild, UNUSED_POINTER); + thread that was marked as current by the prepare handler + , for which we update its + mach port number . */ + GlobalsArenaMap(ThreadRingForkChild); - /* Restart the exception handling thread (see .fork.exc-thread). */ + /* Restart the exception handling thread + . */ protExcThreadStart(); - /* Release all the locks in reverse order, solving (see .fork.lock). */ + /* Release all the locks in reverse order + . */ + GlobalsReinitializeAll(); } @@ -450,7 +440,7 @@ static void protSetupInner(void) AVER(protSetupThread == MACH_PORT_NULL); protExcThreadStart(); - /* Install fork handlers. */ + /* Install fork handlers . */ pthread_atfork(protAtForkPrepare, protAtForkParent, protAtForkChild); } diff --git a/mps/code/th.h b/mps/code/th.h index b5a8b67ba5b..089b14db430 100644 --- a/mps/code/th.h +++ b/mps/code/th.h @@ -75,9 +75,9 @@ extern Res ThreadScan(ScanState ss, Thread thread, Word *stackCold, /* ThreadRingFork* -- atfork handlers for Unix */ -void ThreadRingForkPrepare(Arena arena, void *closure); -void ThreadRingForkParent(Arena arena, void *closure); -void ThreadRingForkChild(Arena arena, void *closure); +void ThreadRingForkPrepare(Arena arena); +void ThreadRingForkParent(Arena arena); +void ThreadRingForkChild(Arena arena); #endif /* th_h */ diff --git a/mps/code/thxc.c b/mps/code/thxc.c index deaff0e2001..54bfd7158c1 100644 --- a/mps/code/thxc.c +++ b/mps/code/thxc.c @@ -205,11 +205,11 @@ static Bool threadForkPrepare(Thread thread) } /* ThreadRingForkPrepare -- prepare for a fork by marking the current - * thread as forking. + * thread as forking . */ -void ThreadRingForkPrepare(Arena arena, void *closure) +void ThreadRingForkPrepare(Arena arena) { - AVER(closure == UNUSED_POINTER); + AVERT(Arena, arena); mapThreadRing(ArenaThreadRing(arena), ArenaDeadRing(arena), threadForkPrepare); } @@ -221,11 +221,11 @@ static Bool threadForkParent(Thread thread) } /* ThreadRingForkParent -- clear the forking flag in the parent after - * a fork. + * a fork . */ -void ThreadRingForkParent(Arena arena, void *closure) +void ThreadRingForkParent(Arena arena) { - AVER(closure == UNUSED_POINTER); + AVERT(Arena, arena); mapThreadRing(ArenaThreadRing(arena), ArenaDeadRing(arena), threadForkParent); } @@ -243,11 +243,12 @@ static Bool threadForkChild(Thread thread) } /* ThreadRingForkChild -- update the mach thread port for the current - * thread; move all other threads to the dead ring. + * thread ; move all other + * threads to the dead ring . */ -void ThreadRingForkChild(Arena arena, void *closure) +void ThreadRingForkChild(Arena arena) { - AVER(closure == UNUSED_POINTER); + AVERT(Arena, arena); mapThreadRing(ArenaThreadRing(arena), ArenaDeadRing(arena), threadForkChild); } diff --git a/mps/design/lock.txt b/mps/design/lock.txt index a6688844cdc..045c44fc3d4 100644 --- a/mps/design/lock.txt +++ b/mps/design/lock.txt @@ -136,6 +136,15 @@ corresponding ``LockClaimRecursive()`` call. Return true if the lock is held by any thread, false otherwise. Note that this function need not be thread-safe (see `.req.held`_). +``void LockInitGlobal(void)`` + +Initialize (or re-initialize) the global locks. This should only be +called in the following circumstances: the first time either of the +global locks is claimed; and in the child process after a ``fork()``. +See design.mps.thread-safety.sol.fork.locks_. + +.. _design.mps.thread-safety.sol.fork.locks: thread-safety#sol-fork-locks + ``void LockClaimGlobal(void)`` Claims ownership of the binary global lock which was previously not diff --git a/mps/design/thread-safety.txt b/mps/design/thread-safety.txt index cba2bade0e1..a31d86c9503 100644 --- a/mps/design/thread-safety.txt +++ b/mps/design/thread-safety.txt @@ -45,6 +45,11 @@ updated at most once (that is, the protocol classes). _`.req.deadlock`: The MPS must not deadlock. +_`.req.fork`: On Unix platforms, the MPS must be able to continue in +the child process after a ``fork()``. (Source: job004062_.) + +.. _job004062: https://www.ravenbrook.com/project/mps/issue/job004062/ + _`.req.perf`: Performance should not be unreasonably hindered. @@ -131,6 +136,38 @@ easiest to implement first, but could be evolved into a `.binary`_ strategy. (That evolution has now happened. tony 1999-08-31). +Fork safety +........... + +In order to support ``fork()``, we need to solve the following problems: + +_`.anal.fork.lock`: Any MPS lock might be held by another thread at +the point where ``fork()`` is called. The lock would be protecting the +integrity of some data structure. But in the child the thread holding +the lock no longer exists, and so there is no way to restore the +integrity. + +_`.anal.fork.threads`: In the child process after a ``fork()``, there +is only one thread, which is a copy of the thread that called +``fork()`` in the parent process. All other threads no longer exist. +But the MPS maintains references to these threads, via the +``ThreadStruct`` object` created by calls to ``mps_thread_reg()``. If +we try to communicate with these threads it will fail or crash. + +_`.anal.fork.exc-thread`: On macOS, the MPS handles protection faults +using a dedicated thread. But in the child process after a ``fork()``, +this dedicated thread no longer exists. Also, the Mach port on which +the dedicated thread receives its messages does not exist in the child +either. + +_`.anal.fork.mach-port`: On macOS, the MPS identifies threads via +their Mach port numbers, which are stashed in the ``ThreadStruct`` and +used to identify the current thread, for example in +``ThreadSuspend()``. But in the child process after ``fork()`` the +running thread has a different Mach port number than it did in the +parent. + + Design ------ @@ -209,6 +246,42 @@ design.mps.sig.check.arg.unlocked_). .. _design.mps.sig.check.arg.unlocked: sig#check-arg-unlocked +Fork safety +----------- + +_`.sol.fork.atfork`: The MPS solves the fork-safety problems by +calling |pthread_atfork|_ to install handler functions that are called +in the parent process just before fork (the "prepare" handler), and in +the parent and child processes just after fork (the "parent" and "child" handlers respectively). + +.. |pthread_atfork| replace: ``pthread_atfork()`` +.. _pthread_atfork: http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_atfork.html + +_`.sol.fork.lock`: In the prepare handler, the MPS takes all the +locks: that is, the global locks, and then the arena lock for every +arena. Note that a side-effect of this is that the shield is entered +for each arena. In the parent handler, the MPS releases all the locks. +In the child handler, the MPS would like to release the locks but this +does not work (neither on macOS nor on Linux), so instead it +reinitializes them. + +_`.sol.fork.threads`: In the prepare handler, the MPS identifies for +each arena the current thread, that is, the one calling ``fork()`` +which will survive into the child process, and marks this thread by +setting a flag in the appropriate ``ThreadStruct``. In the parent +handler, this flag is cleared. In the child handler, all threads +(except for the one flagged) are marked as dead and transferred to the +ring of dead threads. (The MPS can't destroy the thread structures at +this point because they are owned by the client program.) + +_`.sol.fork.exc-thread`: On macOS, in the child handler, the exception +port and dedicated thread are re-created, and the current thread +re-registered with the exception port. + +_`.sol.fork.mach-port`: On macOS, in the child handler, the thread +flagged as forking gets its port number updated. + + Document History ---------------- diff --git a/mps/manual/source/release.rst b/mps/manual/source/release.rst index b694fda4987..7e49fb4ed4e 100644 --- a/mps/manual/source/release.rst +++ b/mps/manual/source/release.rst @@ -4,6 +4,18 @@ Release notes ============= +.. _release-notes-1.117: + +Release 1.117.0 +--------------- + +New features +............ + +#. On FreeBSD, Linux and macOS, the MPS is now able to run in the + child process after ``fork()``. + + .. _release-notes-1.116: Release 1.116.0 From f92f645f6347c4078feae698cfdbe7e83d666908 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 14 Jun 2018 16:47:23 +0100 Subject: [PATCH 13/27] Support for fork on linux and freebsd. Copied from Perforce Change: 193765 --- mps/code/protsgix.c | 32 ++++++++++++++++++++++++++++++++ mps/code/thix.c | 37 ++++++++++++++++++++++++++++++------- 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/mps/code/protsgix.c b/mps/code/protsgix.c index 8bb853e3fce..8ba52d0e66b 100644 --- a/mps/code/protsgix.c +++ b/mps/code/protsgix.c @@ -28,6 +28,7 @@ #include "prmcix.h" +#include /* for pthread_atfork */ #include /* for many functions */ #include /* for ucontext_t */ #include /* for getpid */ @@ -115,6 +116,34 @@ static void sigHandle(int sig, siginfo_t *info, void *uap) /* .sigh.args */ } +/* atfork handlers -- support for fork(). See */ + +static void protAtForkPrepare(void) +{ + /* Take all the locks . */ + GlobalsClaimAll(); +} + +static void protAtForkParent(void) +{ + /* Release all the locks in reverse order + . */ + GlobalsReleaseAll(); +} + +static void protAtForkChild(void) +{ + /* For each arena, move all threads to the dead ring, except for the + thread that was marked as current by the prepare handler + . */ + GlobalsArenaMap(ThreadRingForkChild); + + /* Release all the locks in reverse order + . */ + GlobalsReinitializeAll(); +} + + /* ProtSetup -- global protection setup * * Under Unix, the global setup involves installing a signal handler @@ -133,6 +162,9 @@ void ProtSetup(void) struct sigaction sa; int result; + /* Install fork handlers . */ + pthread_atfork(protAtForkPrepare, protAtForkParent, protAtForkChild); + sa.sa_sigaction = sigHandle; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_SIGINFO; diff --git a/mps/code/thix.c b/mps/code/thix.c index a5207f0f8db..7493677a615 100644 --- a/mps/code/thix.c +++ b/mps/code/thix.c @@ -133,29 +133,26 @@ void ThreadDeregister(Thread thread, Arena arena) /* mapThreadRing -- map over threads on ring calling a function on - * each one except the current thread. + * each one. * * Threads that are found to be dead (that is, if func returns FALSE) - * are moved to deadRing, in order to implement + * are marked as dead and moved to deadRing, in order to implement * design.thread-manager.sol.thread.term.attempt. */ static void mapThreadRing(Ring threadRing, Ring deadRing, Bool (*func)(Thread)) { Ring node, next; - pthread_t self; AVERT(Ring, threadRing); AVERT(Ring, deadRing); AVER(FUNCHECK(func)); - self = pthread_self(); RING_FOR(node, threadRing, next) { Thread thread = RING_ELT(Thread, arenaRing, node); AVERT(Thread, thread); AVER(thread->alive); - if (!pthread_equal(self, thread->id) /* .thread.id */ - && !(*func)(thread)) + if (!(*func)(thread)) { thread->alive = FALSE; RingRemove(&thread->arenaRing); @@ -171,9 +168,14 @@ static void mapThreadRing(Ring threadRing, Ring deadRing, Bool (*func)(Thread)) static Bool threadSuspend(Thread thread) { + Res res; + pthread_t self; + self = pthread_self(); + if (pthread_equal(self, thread->id)) /* .thread.id */ + return TRUE; + /* .error.suspend: if PThreadextSuspend fails, we assume the thread * has been terminated. */ - Res res; AVER(thread->context == NULL); res = PThreadextSuspend(&thread->thrextStruct, &thread->context); AVER(res == ResOK); @@ -196,6 +198,11 @@ void ThreadRingSuspend(Ring threadRing, Ring deadRing) static Bool threadResume(Thread thread) { Res res; + pthread_t self; + self = pthread_self(); + if (pthread_equal(self, thread->id)) /* .thread.id */ + return TRUE; + /* .error.resume: If PThreadextResume fails, we assume the thread * has been terminated. */ AVER(thread->context != NULL); @@ -212,6 +219,22 @@ void ThreadRingResume(Ring threadRing, Ring deadRing) } +static Bool threadForkChild(Thread thread) +{ + return pthread_equal(pthread_self(), thread->id); /* .thread.id */ +} + +/* ThreadRingForkChild -- update the mach thread port for the current + * thread ; move all other + * threads to the dead ring . + */ +void ThreadRingForkChild(Arena arena) +{ + AVERT(Arena, arena); + mapThreadRing(ArenaThreadRing(arena), ArenaDeadRing(arena), threadForkChild); +} + + /* ThreadRingThread -- return the thread at the given ring element */ Thread ThreadRingThread(Ring threadRing) From b5e1965b3d3ccd45ff8d430b4210a34215603754 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 14 Jun 2018 16:54:33 +0100 Subject: [PATCH 14/27] Avoid compilation errors from latest msvc. Copied from Perforce Change: 193768 --- mps/code/amcssth.c | 2 +- mps/code/testlib.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/mps/code/amcssth.c b/mps/code/amcssth.c index d914427eb46..5b15a5535e9 100644 --- a/mps/code/amcssth.c +++ b/mps/code/amcssth.c @@ -193,7 +193,7 @@ static void test_pool(const char *name, mps_pool_t pool, size_t roots_count) size_t condemned = mps_message_gc_condemned_size(arena, msg); size_t not_condemned = mps_message_gc_not_condemned_size(arena, msg); - printf("\nCollection %lu finished:\n", collections++); + printf("\nCollection %lu finished:\n", (unsigned long)collections++); printf("live %"PRIuLONGEST"\n", (ulongest_t)live); printf("condemned %"PRIuLONGEST"\n", (ulongest_t)condemned); printf("not_condemned %"PRIuLONGEST"\n", (ulongest_t)not_condemned); diff --git a/mps/code/testlib.c b/mps/code/testlib.c index 05225b36b75..5be5f17aef0 100644 --- a/mps/code/testlib.c +++ b/mps/code/testlib.c @@ -388,7 +388,6 @@ void error(const char *format, ...) va_start(args, format); verror(format, args); - va_end(args); } From 612b44060f8bb1e834003c4fdc5aaf40c412c142 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 14 Jun 2018 16:58:24 +0100 Subject: [PATCH 15/27] Fix ansi build. Copied from Perforce Change: 193769 --- mps/code/lockan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mps/code/lockan.c b/mps/code/lockan.c index 1c75024fda4..c832032efc7 100644 --- a/mps/code/lockan.c +++ b/mps/code/lockan.c @@ -110,8 +110,8 @@ void LockInitGlobal(void) { globalLock->claims = 0; LockInit(globalLock); - globalRecursiveLock->claims = 0; - LockInit(globalRecursiveLock); + globalRecLock->claims = 0; + LockInit(globalRecLock); } void (LockClaimGlobalRecursive)(void) From 8686ffc625f0ef66b23cb0deb4d5c1f40983708f Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 14 Jun 2018 17:18:08 +0100 Subject: [PATCH 16/27] Add a section on fork safety to the manual. Copied from Perforce Change: 193776 --- mps/manual/source/release.rst | 2 +- mps/manual/source/topic/thread.rst | 37 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/mps/manual/source/release.rst b/mps/manual/source/release.rst index 7e49fb4ed4e..c1adfeb44c2 100644 --- a/mps/manual/source/release.rst +++ b/mps/manual/source/release.rst @@ -13,7 +13,7 @@ New features ............ #. On FreeBSD, Linux and macOS, the MPS is now able to run in the - child process after ``fork()``. + child process after ``fork()``. See :ref:`topic-thread-fork`. .. _release-notes-1.116: diff --git a/mps/manual/source/topic/thread.rst b/mps/manual/source/topic/thread.rst index 96e80c06ea1..a52254382fc 100644 --- a/mps/manual/source/topic/thread.rst +++ b/mps/manual/source/topic/thread.rst @@ -113,6 +113,43 @@ Signal and exception handling issues us `. +.. index:: + single: fork safety + +.. _topic-thread-fork: + +Fork safety +----------- + +On Linux, FreeBSD and macOS, the MPS makes a best-effort attempt to +continue running in the child process after a call to :c:func:`fork`, +even if the :term:`client program` was running multiple +:term:`threads` at the point where the call is made to :c:func:`fork`. + +.. warning:: + + POSIX offers little or nothing in the way of guarantees about the + situation of a child process running after a multi-threaded parent + forked. The specification_ says: + + .. _specification: http://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html + + A process shall be created with a single thread. If a + multi-threaded process calls :c:func:`fork`, the new process shall + contain a replica of the calling thread and its entire address + space, possibly including the states of mutexes and other + resources. Consequently, to avoid errors, the child process may + only execute async-signal-safe operations until such time as one + of the :c:func:`exec` functions is called. + +.. note:: + + Although only one thread is created in the child process, any + threads in the parent process that were registered with the MPS by + calling :c:func:`mps_thread_reg` must still be deregistered, by + calling :c:func:`mps_thread_dereg`, before the arena is destroyed. + + .. index:: single: thread; interface From 6fda83497397fb21cf691b63c52452ccff4e2c65 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 14 Jun 2018 17:44:41 +0100 Subject: [PATCH 17/27] Improve comments. Copied from Perforce Change: 193783 --- mps/code/forktest.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/mps/code/forktest.c b/mps/code/forktest.c index c078bff657e..0426d33e692 100644 --- a/mps/code/forktest.c +++ b/mps/code/forktest.c @@ -1,11 +1,14 @@ -/* tagtest.c: TAGGED POINTER TEST +/* forktest.c: FORK SAFETY TEST * * $Id: //info.ravenbrook.com/project/mps/branch/2018-06-13/fork/code/tagtest.c#1 $ * Copyright (c) 2018 Ravenbrook Limited. See end of file for license. * * .overview: This test case is a regression test for job004062. It * checks that the MPS correctly runs in the child process after a - * fork() on FreeBSD, Linux or OS X. + * fork() on FreeBSD, Linux or macOS. + * + * .format: This test case uses a trivial object format in which each + * object contains a single reference. */ #include @@ -26,9 +29,9 @@ enum { typedef struct obj_s { unsigned type; /* One of the TYPE_ enums */ union { - struct obj_s *ref; - mps_addr_t fwd; - size_t pad; + struct obj_s *ref; /* TYPE_REF */ + mps_addr_t fwd; /* TYPE_FWD */ + size_t pad; /* TYPE_PAD */ } u; } obj_s, *obj_t; @@ -108,7 +111,8 @@ int main(int argc, char *argv[]) testlib_init(argc, argv); /* Set the pause time to be very small so that the incremental - collector has to leave a read barrier in place for us to hit. */ + collector (when it runs) will have to leave a read barrier in + place for us to hit. */ MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD(args, MPS_KEY_PAUSE_TIME, 0.0); die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), @@ -139,7 +143,7 @@ int main(int argc, char *argv[]) die(mps_ap_create_k(&obj_ap, pool, mps_args_none), "Couldn't create obj allocation point"); - /* Create a linked list of a million cells. */ + /* Create a linked list of objects. */ first = NULL; for (i = 0; i < 100000; ++i) { size_t size = ALIGN_UP(sizeof(obj_s), ALIGNMENT); @@ -157,17 +161,20 @@ int main(int argc, char *argv[]) pid = fork(); cdie(pid >= 0, "fork failed"); if (pid == 0) { - /* Child: allow a collection to start */ + /* Child: allow a collection to start, which will cause a read + barrier to be applied to any segment containing live objects + that was scanned. */ mps_arena_release(arena); - /* Read a bunch of stuff so that we hit the read barrier. */ + /* Read all the objects, so that if there is read barrier in place + we will hit it. */ for (obj = first; obj != NULL; obj = obj->u.ref) { Insist(obj->type == TYPE_REF); } mps_arena_park(arena); } else { - /* Parent: wait for child result */ + /* Parent: wait for child and check that its exit status is zero. */ int stat; cdie(pid == waitpid(pid, &stat, 0), "waitpid failed"); cdie(WIFEXITED(stat), "child did not exit normally"); From 3829b5a2a1f6d7237733eb2672f3caf081790e20 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 14 Jun 2018 17:50:29 +0100 Subject: [PATCH 18/27] Fix rst error; only need to flag forking thraed on macos. Copied from Perforce Change: 193784 --- mps/design/thread-safety.txt | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/mps/design/thread-safety.txt b/mps/design/thread-safety.txt index a31d86c9503..e0f6a3f03ec 100644 --- a/mps/design/thread-safety.txt +++ b/mps/design/thread-safety.txt @@ -45,7 +45,7 @@ updated at most once (that is, the protocol classes). _`.req.deadlock`: The MPS must not deadlock. -_`.req.fork`: On Unix platforms, the MPS must be able to continue in +_`.req.fork`: On Unix platforms, the MPS should be able to continue in the child process after a ``fork()``. (Source: job004062_.) .. _job004062: https://www.ravenbrook.com/project/mps/issue/job004062/ @@ -250,11 +250,12 @@ Fork safety ----------- _`.sol.fork.atfork`: The MPS solves the fork-safety problems by -calling |pthread_atfork|_ to install handler functions that are called -in the parent process just before fork (the "prepare" handler), and in -the parent and child processes just after fork (the "parent" and "child" handlers respectively). +calling |pthread_atfork|_ to install handler functions that are +called in the parent process just before fork (the "prepare" handler), +and in the parent and child processes just after fork (the "parent" +and "child" handlers respectively). -.. |pthread_atfork| replace: ``pthread_atfork()`` +.. |pthread_atfork| replace:: ``pthread_atfork()`` .. _pthread_atfork: http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_atfork.html _`.sol.fork.lock`: In the prepare handler, the MPS takes all the @@ -265,14 +266,15 @@ In the child handler, the MPS would like to release the locks but this does not work (neither on macOS nor on Linux), so instead it reinitializes them. -_`.sol.fork.threads`: In the prepare handler, the MPS identifies for -each arena the current thread, that is, the one calling ``fork()`` -which will survive into the child process, and marks this thread by -setting a flag in the appropriate ``ThreadStruct``. In the parent -handler, this flag is cleared. In the child handler, all threads -(except for the one flagged) are marked as dead and transferred to the -ring of dead threads. (The MPS can't destroy the thread structures at -this point because they are owned by the client program.) +_`.sol.fork.threads`: On macOS, in the prepare handler, the MPS +identifies for each arena the current thread, that is, the one calling +``fork()`` which will survive into the child process, and marks this +thread by setting a flag in the appropriate ``ThreadStruct``. In the +parent handler, this flag is cleared. On all Unix platforms, in the +child handler, all threads (except for the current thread) are marked +as dead and transferred to the ring of dead threads. (The MPS can't +destroy the thread structures at this point because they are owned by +the client program.) _`.sol.fork.exc-thread`: On macOS, in the child handler, the exception port and dedicated thread are re-created, and the current thread From b1ac192a9a440ce38daf252d3a979a6b2b597280 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 14 Jun 2018 17:55:03 +0100 Subject: [PATCH 19/27] Update design history. restore accidentally removed check. Copied from Perforce Change: 193787 --- mps/code/global.c | 2 ++ mps/design/lock.txt | 2 ++ mps/design/thread-safety.txt | 6 ++++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/mps/code/global.c b/mps/code/global.c index 73b7c2fb5df..8e17c8a9f88 100644 --- a/mps/code/global.c +++ b/mps/code/global.c @@ -145,6 +145,7 @@ static void arenaDenounce(Arena arena) void GlobalsArenaMap(void (*func)(Arena arena)) { Ring node, nextNode; + AVERT(Ring, &arenaRing); RING_FOR(node, &arenaRing, nextNode) { Globals arenaGlobals = RING_ELT(Globals, globalRing, node); Arena arena = GlobalsArena(arenaGlobals); @@ -644,6 +645,7 @@ Bool ArenaAccess(Addr addr, AccessSet mode, MutatorContext context) Res res; arenaClaimRingLock(); /* */ + AVERT(Ring, &arenaRing); RING_FOR(node, &arenaRing, nextNode) { Globals arenaGlobals = RING_ELT(Globals, globalRing, node); diff --git a/mps/design/lock.txt b/mps/design/lock.txt index 045c44fc3d4..7df480746a0 100644 --- a/mps/design/lock.txt +++ b/mps/design/lock.txt @@ -287,6 +287,8 @@ Document History - 2014-10-21 GDR_ Brought up to date. +- 2018-06-14 GDR_ Added ``LockInitGlobal()``. + .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ diff --git a/mps/design/thread-safety.txt b/mps/design/thread-safety.txt index e0f6a3f03ec..f74a536b4f7 100644 --- a/mps/design/thread-safety.txt +++ b/mps/design/thread-safety.txt @@ -263,8 +263,8 @@ locks: that is, the global locks, and then the arena lock for every arena. Note that a side-effect of this is that the shield is entered for each arena. In the parent handler, the MPS releases all the locks. In the child handler, the MPS would like to release the locks but this -does not work (neither on macOS nor on Linux), so instead it -reinitializes them. +does not work on any supported platform, so instead it reinitializes +them, by calling ``LockInitGlobal()``. _`.sol.fork.threads`: On macOS, in the prepare handler, the MPS identifies for each arena the current thread, that is, the one calling @@ -293,6 +293,8 @@ Document History - 2013-05-22 GDR_ Converted to reStructuredText. +- 2018-06-14 GDR_ Added fork safety design. + .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ From ad432cf7c46122fa5a8743508b1df8a60d7aead4 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 14 Jun 2018 19:31:30 +0100 Subject: [PATCH 20/27] Fix issues identified in review by nb. Copied from Perforce Change: 193793 --- mps/code/forktest.c | 11 +++-------- mps/code/protsgix.c | 9 +++------ mps/code/testlib.c | 1 + mps/code/thix.c | 8 +++----- 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/mps/code/forktest.c b/mps/code/forktest.c index 0426d33e692..6d87043af2c 100644 --- a/mps/code/forktest.c +++ b/mps/code/forktest.c @@ -35,11 +35,6 @@ typedef struct obj_s { } u; } obj_s, *obj_t; -#define ALIGNMENT sizeof(obj_s) - -/* Align up a to a multiple of b. */ -#define ALIGN_UP(a, b) (((a) + (b) - 1) & ~((b) - 1)) - static void obj_fwd(mps_addr_t old, mps_addr_t new) { obj_t obj = old; @@ -73,7 +68,7 @@ static mps_addr_t obj_skip(mps_addr_t addr) } else { size = sizeof(obj_s); } - return (char *)addr + ALIGN_UP(size, ALIGNMENT); + return (char *)addr + size; } static mps_res_t obj_scan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) @@ -125,7 +120,7 @@ int main(int argc, char *argv[]) "Couldn't create thread root"); MPS_ARGS_BEGIN(args) { - MPS_ARGS_ADD(args, MPS_KEY_FMT_ALIGN, ALIGNMENT); + MPS_ARGS_ADD(args, MPS_KEY_FMT_ALIGN, sizeof(obj_s)); MPS_ARGS_ADD(args, MPS_KEY_FMT_SCAN, obj_scan); MPS_ARGS_ADD(args, MPS_KEY_FMT_SKIP, obj_skip); MPS_ARGS_ADD(args, MPS_KEY_FMT_FWD, obj_fwd); @@ -146,7 +141,7 @@ int main(int argc, char *argv[]) /* Create a linked list of objects. */ first = NULL; for (i = 0; i < 100000; ++i) { - size_t size = ALIGN_UP(sizeof(obj_s), ALIGNMENT); + size_t size = sizeof(obj_s); mps_addr_t addr; do { die(mps_reserve(&addr, obj_ap, size), "Couldn't allocate."); diff --git a/mps/code/protsgix.c b/mps/code/protsgix.c index 8ba52d0e66b..5a1c374032f 100644 --- a/mps/code/protsgix.c +++ b/mps/code/protsgix.c @@ -126,20 +126,17 @@ static void protAtForkPrepare(void) static void protAtForkParent(void) { - /* Release all the locks in reverse order - . */ + /* Release all the locks . */ GlobalsReleaseAll(); } static void protAtForkChild(void) { /* For each arena, move all threads to the dead ring, except for the - thread that was marked as current by the prepare handler - . */ + current thread . */ GlobalsArenaMap(ThreadRingForkChild); - /* Release all the locks in reverse order - . */ + /* Reinitialize all the locks . */ GlobalsReinitializeAll(); } diff --git a/mps/code/testlib.c b/mps/code/testlib.c index 5be5f17aef0..cd8f340ba36 100644 --- a/mps/code/testlib.c +++ b/mps/code/testlib.c @@ -388,6 +388,7 @@ void error(const char *format, ...) va_start(args, format); verror(format, args); + /* va_end(args); */ /* provokes "unreachable code" error from MSVC */ } diff --git a/mps/code/thix.c b/mps/code/thix.c index 7493677a615..831edd0c544 100644 --- a/mps/code/thix.c +++ b/mps/code/thix.c @@ -152,8 +152,7 @@ static void mapThreadRing(Ring threadRing, Ring deadRing, Bool (*func)(Thread)) Thread thread = RING_ELT(Thread, arenaRing, node); AVERT(Thread, thread); AVER(thread->alive); - if (!(*func)(thread)) - { + if (!(*func)(thread)) { thread->alive = FALSE; RingRemove(&thread->arenaRing); RingAppend(deadRing, &thread->arenaRing); @@ -224,9 +223,8 @@ static Bool threadForkChild(Thread thread) return pthread_equal(pthread_self(), thread->id); /* .thread.id */ } -/* ThreadRingForkChild -- update the mach thread port for the current - * thread ; move all other - * threads to the dead ring . +/* ThreadRingForkChild -- move threads except for the current thread + * to the dead ring . */ void ThreadRingForkChild(Arena arena) { From 4eeac719ad5d8e42c618edf4f89e9a551c375bad Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Fri, 15 Jun 2018 10:06:59 +0100 Subject: [PATCH 21/27] Start the collection and dereference the objects in the parent as well as the child. Copied from Perforce Change: 193800 --- mps/code/forktest.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/mps/code/forktest.c b/mps/code/forktest.c index 6d87043af2c..e1d1909d4c1 100644 --- a/mps/code/forktest.c +++ b/mps/code/forktest.c @@ -155,20 +155,21 @@ int main(int argc, char *argv[]) pid = fork(); cdie(pid >= 0, "fork failed"); - if (pid == 0) { - /* Child: allow a collection to start, which will cause a read - barrier to be applied to any segment containing live objects - that was scanned. */ - mps_arena_release(arena); - /* Read all the objects, so that if there is read barrier in place - we will hit it. */ - for (obj = first; obj != NULL; obj = obj->u.ref) { - Insist(obj->type == TYPE_REF); - } + /* Allow a collection to start, which will cause a read barrier to + be applied to any segment containing live objects that was + scanned. */ + mps_arena_release(arena); - mps_arena_park(arena); - } else { + /* Read all the objects, so that if there is read barrier in place + we will hit it. */ + for (obj = first; obj != NULL; obj = obj->u.ref) { + Insist(obj->type == TYPE_REF); + } + + mps_arena_park(arena); + + if (pid != 0) { /* Parent: wait for child and check that its exit status is zero. */ int stat; cdie(pid == waitpid(pid, &stat, 0), "waitpid failed"); From b0a73a5c71ca7568f0f02b9ce0a25f9e22e93b3a Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Fri, 15 Jun 2018 10:58:33 +0100 Subject: [PATCH 22/27] Refactor atfork code to improve separation of concerns. Copied from Perforce Change: 193806 --- mps/code/global.c | 8 +- mps/code/lock.h | 5 ++ mps/code/lockan.c | 5 ++ mps/code/lockix.c | 10 +++ mps/code/lockw3.c | 4 + mps/code/protsgix.c | 29 -------- mps/code/protxc.c | 23 ------ mps/code/th.h | 7 +- mps/code/than.c | 6 ++ mps/code/thix.c | 42 +++++++---- mps/code/thw3.c | 6 ++ mps/code/thxc.c | 140 +++++++++++++++++++++-------------- mps/design/lock.txt | 9 ++- mps/design/thread-safety.txt | 2 +- 14 files changed, 163 insertions(+), 133 deletions(-) diff --git a/mps/code/global.c b/mps/code/global.c index 8e17c8a9f88..aab86d2dcc4 100644 --- a/mps/code/global.c +++ b/mps/code/global.c @@ -87,8 +87,8 @@ static void arenaReinitLock(Arena arena) void GlobalsReinitializeAll(void) { - LockInitGlobal(); GlobalsArenaMap(arenaReinitLock); + LockInitGlobal(); } @@ -275,7 +275,13 @@ Res GlobalsInit(Globals arenaGlobals) arenaRingInit = TRUE; RingInit(&arenaRing); arenaSerial = (Serial)0; + /* The setup functions call pthread_atfork (on the appropriate + platforms) and so must be called in the correct order. Here we + require the locks to be taken first in the "prepare" case and + released last in the "parent" and "child" cases. */ + ThreadSetup(); ProtSetup(); + LockSetup(); } arena = GlobalsArena(arenaGlobals); /* Ensure updates to arenaSerial do not race by doing the update diff --git a/mps/code/lock.h b/mps/code/lock.h index 9c3b9c080d0..32664d2262a 100644 --- a/mps/code/lock.h +++ b/mps/code/lock.h @@ -133,6 +133,11 @@ extern void LockClaimGlobal(void); extern void LockReleaseGlobal(void); +/* LockSetup -- one-time lock initialization */ + +extern void LockSetup(void); + + #endif /* lock_h */ diff --git a/mps/code/lockan.c b/mps/code/lockan.c index c832032efc7..061b63d20a9 100644 --- a/mps/code/lockan.c +++ b/mps/code/lockan.c @@ -134,6 +134,11 @@ void (LockReleaseGlobal)(void) LockRelease(globalLock); } +void LockSetup(void) +{ + /* Nothing to do as ANSI platform does not have fork(). */ +} + /* C. COPYRIGHT AND LICENSE * diff --git a/mps/code/lockix.c b/mps/code/lockix.c index 2b80a165456..4cc89b6e79e 100644 --- a/mps/code/lockix.c +++ b/mps/code/lockix.c @@ -261,6 +261,16 @@ void (LockReleaseGlobal)(void) } +/* LockSetup -- one-time lock initialization */ + +void LockSetup(void) +{ + /* Claim all locks before a fork; release in the parent; + reinitialize in the child */ + pthread_atfork(GlobalsClaimAll, GlobalsReleaseAll, GlobalsReinitializeAll); +} + + #elif defined(LOCK_NONE) #include "lockan.c" #else diff --git a/mps/code/lockw3.c b/mps/code/lockw3.c index cbd38e4ed7e..9819d202dd2 100644 --- a/mps/code/lockw3.c +++ b/mps/code/lockw3.c @@ -171,6 +171,10 @@ void (LockReleaseGlobal)(void) LockRelease(globalLock); } +void LockSetup(void) +{ + /* Nothing to do as MPS does not support fork() on Windows. */ +} #elif defined(LOCK_NONE) #include "lockan.c" diff --git a/mps/code/protsgix.c b/mps/code/protsgix.c index 5a1c374032f..8bb853e3fce 100644 --- a/mps/code/protsgix.c +++ b/mps/code/protsgix.c @@ -28,7 +28,6 @@ #include "prmcix.h" -#include /* for pthread_atfork */ #include /* for many functions */ #include /* for ucontext_t */ #include /* for getpid */ @@ -116,31 +115,6 @@ static void sigHandle(int sig, siginfo_t *info, void *uap) /* .sigh.args */ } -/* atfork handlers -- support for fork(). See */ - -static void protAtForkPrepare(void) -{ - /* Take all the locks . */ - GlobalsClaimAll(); -} - -static void protAtForkParent(void) -{ - /* Release all the locks . */ - GlobalsReleaseAll(); -} - -static void protAtForkChild(void) -{ - /* For each arena, move all threads to the dead ring, except for the - current thread . */ - GlobalsArenaMap(ThreadRingForkChild); - - /* Reinitialize all the locks . */ - GlobalsReinitializeAll(); -} - - /* ProtSetup -- global protection setup * * Under Unix, the global setup involves installing a signal handler @@ -159,9 +133,6 @@ void ProtSetup(void) struct sigaction sa; int result; - /* Install fork handlers . */ - pthread_atfork(protAtForkPrepare, protAtForkParent, protAtForkChild); - sa.sa_sigaction = sigHandle; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_SIGINFO; diff --git a/mps/code/protxc.c b/mps/code/protxc.c index 4792fcdb4b2..733afcc48c5 100644 --- a/mps/code/protxc.c +++ b/mps/code/protxc.c @@ -396,40 +396,17 @@ static void protExcThreadStart(void) static void protAtForkPrepare(void) { - /* Take all the locks . */ - GlobalsClaimAll(); - - /* For each arena, remember which thread is the current thread, if - any . */ - GlobalsArenaMap(ThreadRingForkPrepare); } static void protAtForkParent(void) { - /* For each arena, mark threads as not forking any more - . */ - GlobalsArenaMap(ThreadRingForkParent); - - /* Release all the locks in reverse order - . */ - GlobalsReleaseAll(); } static void protAtForkChild(void) { - /* For each arena, move all threads to the dead ring, except for the - thread that was marked as current by the prepare handler - , for which we update its - mach port number . */ - GlobalsArenaMap(ThreadRingForkChild); - /* Restart the exception handling thread . */ protExcThreadStart(); - - /* Release all the locks in reverse order - . */ - GlobalsReinitializeAll(); } diff --git a/mps/code/th.h b/mps/code/th.h index 089b14db430..83f15d868c9 100644 --- a/mps/code/th.h +++ b/mps/code/th.h @@ -72,12 +72,7 @@ extern Res ThreadScan(ScanState ss, Thread thread, Word *stackCold, mps_area_scan_t scan_area, void *closure); - -/* ThreadRingFork* -- atfork handlers for Unix */ - -void ThreadRingForkPrepare(Arena arena); -void ThreadRingForkParent(Arena arena); -void ThreadRingForkChild(Arena arena); +extern void ThreadSetup(void); #endif /* th_h */ diff --git a/mps/code/than.c b/mps/code/than.c index dc4781dd6cd..af434096c6f 100644 --- a/mps/code/than.c +++ b/mps/code/than.c @@ -142,6 +142,12 @@ Res ThreadDescribe(Thread thread, mps_lib_FILE *stream, Count depth) } +void ThreadSetup(void) +{ + /* Nothing to do as ANSI platform does not have fork(). */ +} + + /* C. COPYRIGHT AND LICENSE * * Copyright (C) 2001-2014 Ravenbrook Limited . diff --git a/mps/code/thix.c b/mps/code/thix.c index 831edd0c544..85e4009b16c 100644 --- a/mps/code/thix.c +++ b/mps/code/thix.c @@ -218,21 +218,6 @@ void ThreadRingResume(Ring threadRing, Ring deadRing) } -static Bool threadForkChild(Thread thread) -{ - return pthread_equal(pthread_self(), thread->id); /* .thread.id */ -} - -/* ThreadRingForkChild -- move threads except for the current thread - * to the dead ring . - */ -void ThreadRingForkChild(Arena arena) -{ - AVERT(Arena, arena); - mapThreadRing(ArenaThreadRing(arena), ArenaDeadRing(arena), threadForkChild); -} - - /* ThreadRingThread -- return the thread at the given ring element */ Thread ThreadRingThread(Ring threadRing) @@ -328,6 +313,33 @@ Res ThreadDescribe(Thread thread, mps_lib_FILE *stream, Count depth) } +/* threadAtForkChild -- for each arena, move threads except for the + * current thread to the dead ring . + */ + +static Bool threadForkChild(Thread thread) +{ + AVERT(Thread, thread); + return pthread_equal(pthread_self(), thread->id); /* .thread.id */ +} + +static void threadRingForkChild(Arena arena) +{ + AVERT(Arena, arena); + mapThreadRing(ArenaThreadRing(arena), ArenaDeadRing(arena), threadForkChild); +} + +static void threadAtForkChild(void) +{ + GlobalsArenaMap(ThreadRingForkChild); +} + +void ThreadSetup(void) +{ + pthread_atfork(NULL, NULL, threadAtForkChild); +} + + /* C. COPYRIGHT AND LICENSE * * Copyright (C) 2001-2016 Ravenbrook Limited . diff --git a/mps/code/thw3.c b/mps/code/thw3.c index aeb395bbcab..b09c03decb1 100644 --- a/mps/code/thw3.c +++ b/mps/code/thw3.c @@ -312,6 +312,12 @@ Res ThreadScan(ScanState ss, Thread thread, Word *stackCold, } +void ThreadSetup(void) +{ + /* Nothing to do as MPS does not support fork() on Windows. */ +} + + /* C. COPYRIGHT AND LICENSE * * Copyright (C) 2001-2016 Ravenbrook Limited . diff --git a/mps/code/thxc.c b/mps/code/thxc.c index 54bfd7158c1..403a35a2883 100644 --- a/mps/code/thxc.c +++ b/mps/code/thxc.c @@ -26,6 +26,7 @@ #include #include #include +#include SRCID(thxc, "$Id$"); @@ -197,62 +198,6 @@ void ThreadRingResume(Ring threadRing, Ring deadRing) } -static Bool threadForkPrepare(Thread thread) -{ - AVER(!thread->forking); - thread->forking = (thread->port == mach_thread_self()); - return TRUE; -} - -/* ThreadRingForkPrepare -- prepare for a fork by marking the current - * thread as forking . - */ -void ThreadRingForkPrepare(Arena arena) -{ - AVERT(Arena, arena); - mapThreadRing(ArenaThreadRing(arena), ArenaDeadRing(arena), threadForkPrepare); -} - - -static Bool threadForkParent(Thread thread) -{ - thread->forking = FALSE; - return TRUE; -} - -/* ThreadRingForkParent -- clear the forking flag in the parent after - * a fork . - */ -void ThreadRingForkParent(Arena arena) -{ - AVERT(Arena, arena); - mapThreadRing(ArenaThreadRing(arena), ArenaDeadRing(arena), threadForkParent); -} - - -static Bool threadForkChild(Thread thread) -{ - if (thread->forking) { - thread->port = mach_thread_self(); - AVER(MACH_PORT_VALID(thread->port)); - thread->forking = FALSE; - return TRUE; - } else { - return FALSE; - } -} - -/* ThreadRingForkChild -- update the mach thread port for the current - * thread ; move all other - * threads to the dead ring . - */ -void ThreadRingForkChild(Arena arena) -{ - AVERT(Arena, arena); - mapThreadRing(ArenaThreadRing(arena), ArenaDeadRing(arena), threadForkChild); -} - - Thread ThreadRingThread(Ring threadRing) { Thread thread; @@ -358,6 +303,89 @@ Res ThreadDescribe(Thread thread, mps_lib_FILE *stream, Count depth) } +/* threadAtForkPrepare -- for each arena, mark the current thread as + * forking . + */ + +static Bool threadForkPrepare(Thread thread) +{ + AVERT(Thread, thread); + AVER(!thread->forking); + thread->forking = (thread->port == mach_thread_self()); + return TRUE; +} + +static void threadRingForkPrepare(Arena arena) +{ + AVERT(Arena, arena); + mapThreadRing(ArenaThreadRing(arena), ArenaDeadRing(arena), threadForkPrepare); +} + +static void threadAtForkPrepare(void) +{ + GlobalsArenaMap(threadRingForkPrepare); +} + + +/* threadAtForkParent -- for each arena, clear the forking flag for + * all threads . + */ + +static Bool threadForkParent(Thread thread) +{ + AVERT(Thread, thread); + thread->forking = FALSE; + return TRUE; +} + +static void threadRingForkParent(Arena arena) +{ + AVERT(Arena, arena); + mapThreadRing(ArenaThreadRing(arena), ArenaDeadRing(arena), threadForkParent); +} + +static void threadAtForkParent(void) +{ + GlobalsArenaMap(threadRingForkParent); +} + + +/* threadAtForkChild -- For each arena, move all threads to the dead + * ring, except for the thread that was marked as forking by the + * prepare handler , for which + * update its mach port . + */ + +static Bool threadForkChild(Thread thread) +{ + AVERT(Thread, thread); + if (thread->forking) { + thread->port = mach_thread_self(); + AVER(MACH_PORT_VALID(thread->port)); + thread->forking = FALSE; + return TRUE; + } else { + return FALSE; + } +} + +static void threadRingForkChild(Arena arena) +{ + AVERT(Arena, arena); + mapThreadRing(ArenaThreadRing(arena), ArenaDeadRing(arena), threadForkChild); +} + +static void threadAtForkChild(void) +{ + GlobalsArenaMap(threadRingForkChild); +} + +void ThreadSetup(void) +{ + pthread_atfork(threadAtForkPrepare, threadAtForkParent, threadAtForkChild); +} + + /* C. COPYRIGHT AND LICENSE * * Copyright (C) 2001-2016 Ravenbrook Limited . diff --git a/mps/design/lock.txt b/mps/design/lock.txt index 7df480746a0..9d390a1a8bd 100644 --- a/mps/design/lock.txt +++ b/mps/design/lock.txt @@ -141,9 +141,9 @@ that this function need not be thread-safe (see `.req.held`_). Initialize (or re-initialize) the global locks. This should only be called in the following circumstances: the first time either of the global locks is claimed; and in the child process after a ``fork()``. -See design.mps.thread-safety.sol.fork.locks_. +See design.mps.thread-safety.sol.fork.lock_. -.. _design.mps.thread-safety.sol.fork.locks: thread-safety#sol-fork-locks +.. _design.mps.thread-safety.sol.fork.lock: thread-safety#sol-fork-lock ``void LockClaimGlobal(void)`` @@ -164,6 +164,11 @@ to the current thread and claims the lock (if not already held). Restores the previous state of the recursive global lock remembered by the corresponding ``LockClaimGlobalRecursive()`` call. +``void LockSetup(void)`` + +One-time initialization function, intended for calling +``pthread_atfork()`` on the appropriate platforms: see design.mps.thread-safety.sol.fork.lock_. + Implementation -------------- diff --git a/mps/design/thread-safety.txt b/mps/design/thread-safety.txt index f74a536b4f7..6db199ebbb5 100644 --- a/mps/design/thread-safety.txt +++ b/mps/design/thread-safety.txt @@ -266,7 +266,7 @@ In the child handler, the MPS would like to release the locks but this does not work on any supported platform, so instead it reinitializes them, by calling ``LockInitGlobal()``. -_`.sol.fork.threads`: On macOS, in the prepare handler, the MPS +_`.sol.fork.thread`: On macOS, in the prepare handler, the MPS identifies for each arena the current thread, that is, the one calling ``fork()`` which will survive into the child process, and marks this thread by setting a flag in the appropriate ``ThreadStruct``. In the From 81874fdf08393b03148a348464d7068b51326c6a Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Fri, 15 Jun 2018 11:15:42 +0100 Subject: [PATCH 23/27] Fix typo. Copied from Perforce Change: 193811 --- mps/code/thix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mps/code/thix.c b/mps/code/thix.c index 85e4009b16c..5d0f55565af 100644 --- a/mps/code/thix.c +++ b/mps/code/thix.c @@ -331,7 +331,7 @@ static void threadRingForkChild(Arena arena) static void threadAtForkChild(void) { - GlobalsArenaMap(ThreadRingForkChild); + GlobalsArenaMap(threadRingForkChild); } void ThreadSetup(void) From 307bc47730220348e9b585e5d23a51b2fc9e4304 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Fri, 15 Jun 2018 12:15:36 +0100 Subject: [PATCH 24/27] Rename "mac os x" and "os x" to "macos", except for a few cases where for historical accuracy we want to continue to refer to the former name. Copied from Perforce Change: 193821 --- mps/code/config.h | 10 +++++----- mps/code/lockix.c | 6 +++--- mps/code/mps.c | 26 ++++++++++++------------ mps/code/prmcxc.c | 6 +++--- mps/code/prmcxc.h | 6 +++--- mps/code/prmcxci3.c | 6 +++--- mps/code/prmcxci6.c | 6 +++--- mps/code/protix.c | 8 ++++---- mps/code/protxc.c | 16 +++++++-------- mps/code/protxc.h | 6 +++--- mps/code/sc.h | 12 +++++------ mps/code/shield.c | 6 +++--- mps/code/ssixi3.c | 10 +++++----- mps/code/ssixi6.c | 16 +++++++-------- mps/code/thxc.c | 8 ++++---- mps/code/vmix.c | 8 ++++---- mps/code/xci3gc.gmk | 6 +++--- mps/code/xci3ll.gmk | 6 +++--- mps/code/xci6gc.gmk | 6 +++--- mps/code/xci6ll.gmk | 6 +++--- mps/design/prmc.txt | 6 +++--- mps/design/prot.txt | 4 ++-- mps/design/shield.txt | 6 +++--- mps/design/thread-manager.txt | 6 +++--- mps/design/vm.txt | 4 ++-- mps/design/write-barrier.txt | 4 ++-- mps/manual/build.txt | 30 ++++++++++++++-------------- mps/manual/source/code-index.rst | 15 +++++++------- mps/manual/source/glossary/m.rst | 4 ++-- mps/manual/source/guide/debug.rst | 8 ++++---- mps/manual/source/guide/overview.rst | 2 +- mps/manual/source/pool/awl.rst | 2 +- mps/manual/source/topic/platform.rst | 12 +++++------ mps/manual/source/topic/plinth.rst | 2 +- mps/manual/source/topic/porting.rst | 6 +++--- mps/manual/source/topic/security.rst | 2 +- mps/manual/source/topic/thread.rst | 2 +- mps/procedure/release-build.rst | 2 +- mps/readme.txt | 6 +++--- mps/test/test/script/platform | 2 +- mps/test/test/script/version | 2 +- mps/tool/index.rst | 6 +++--- 42 files changed, 157 insertions(+), 156 deletions(-) diff --git a/mps/code/config.h b/mps/code/config.h index a4f1bc4be89..d30ff3d9211 100644 --- a/mps/code/config.h +++ b/mps/code/config.h @@ -1,7 +1,7 @@ /* config.h: MPS CONFIGURATION * * $Id$ - * Copyright (c) 2001-2017 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. * * PURPOSE @@ -546,7 +546,7 @@ #endif -/* .feature.xc: OS X feature specification +/* .feature.xc: macOS feature specification * * The MPS needs the following symbols which are not defined by default * @@ -589,7 +589,7 @@ #else -#error "Unknown OS X architecture" +#error "Unknown macOS architecture" #endif #endif @@ -694,7 +694,7 @@ * * TODO: These settings were determined by trial and error, but should * be based on measurement of the protection overhead on each - * platform. We know it's extremely different between OS X and + * platform. We know it's extremely different between macOS and * Windows, for example. See design.mps.write-barrier.improv.by-os. * * TODO: Consider basing the count on the amount of time that has @@ -712,7 +712,7 @@ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2017 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/lockix.c b/mps/code/lockix.c index 4cc89b6e79e..7d88855a9bb 100644 --- a/mps/code/lockix.c +++ b/mps/code/lockix.c @@ -1,7 +1,7 @@ /* lockix.c: RECURSIVE LOCKS FOR POSIX SYSTEMS * * $Id$ - * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * .posix: The implementation uses a POSIX interface, and should be reusable * for many Unix-like operating systems. @@ -9,7 +9,7 @@ * .freebsd: This implementation supports FreeBSD (platform * MPS_OS_FR). * - * .darwin: This implementation supports Darwin (OS X) (platform + * .darwin: This implementation supports Darwin (macOS) (platform * MPS_OS_XC). * * .design: These locks are implemented using mutexes. @@ -280,7 +280,7 @@ void LockSetup(void) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2016 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/mps.c b/mps/code/mps.c index eec3e586e8e..2790e5f9f1b 100644 --- a/mps/code/mps.c +++ b/mps/code/mps.c @@ -1,14 +1,14 @@ /* mps.c: MEMORY POOL SYSTEM ALL-IN-ONE TRANSLATION UNIT * * $Id$ - * Copyright (C) 2012-2016 Ravenbrook Limited. See end of file for license. + * Copyright (C) 2012-2018 Ravenbrook Limited. See end of file for license. * * .purpose: This file can be compiled to create the complete MPS library in * a single compilation, allowing the compiler to apply global optimizations * and inlining effectively. On most modern compilers this is also faster * than compiling each file separately. * - * .purpose.universal: This file also allows simple building of a Mac OS X + * .purpose.universal: This file also allows simple building of a macOS * "universal" (multiple architecture) binary when the set of source files * differs by architecture. It may work for other platforms in a similar * manner. @@ -111,33 +111,33 @@ #include "span.c" /* generic stack probe */ #include "ssan.c" /* generic stack scanner */ -/* Mac OS X on 32-bit Intel built with Clang or GCC */ +/* macOS on 32-bit Intel built with Clang or GCC */ #elif defined(MPS_PF_XCI3LL) || defined(MPS_PF_XCI3GC) #include "lockix.c" /* Posix locks */ -#include "thxc.c" /* OS X Mach threading */ +#include "thxc.c" /* macOS Mach threading */ #include "vmix.c" /* Posix virtual memory */ #include "protix.c" /* Posix protection */ -#include "protxc.c" /* OS X Mach exception handling */ +#include "protxc.c" /* macOS Mach exception handling */ #include "prmci3.c" /* 32-bit Intel mutator context decoding */ -#include "prmcxc.c" /* Mac OS X mutator context */ -#include "prmcxci3.c" /* 32-bit Intel for Mac OS X mutator context */ +#include "prmcxc.c" /* macOS mutator context */ +#include "prmcxci3.c" /* 32-bit Intel for macOS mutator context */ #include "span.c" /* generic stack probe */ #include "ssixi3.c" /* Posix on 32-bit Intel stack scan */ -/* Mac OS X on 64-bit Intel build with Clang or GCC */ +/* macOS on 64-bit Intel build with Clang or GCC */ #elif defined(MPS_PF_XCI6LL) || defined(MPS_PF_XCI6GC) #include "lockix.c" /* Posix locks */ -#include "thxc.c" /* OS X Mach threading */ +#include "thxc.c" /* macOS Mach threading */ #include "vmix.c" /* Posix virtual memory */ #include "protix.c" /* Posix protection */ -#include "protxc.c" /* OS X Mach exception handling */ +#include "protxc.c" /* macOS Mach exception handling */ #include "prmci6.c" /* 64-bit Intel mutator context decoding */ -#include "prmcxc.c" /* Mac OS X mutator context */ -#include "prmcxci6.c" /* 64-bit Intel for Mac OS X mutator context */ +#include "prmcxc.c" /* macOS mutator context */ +#include "prmcxci6.c" /* 64-bit Intel for macOS mutator context */ #include "span.c" /* generic stack probe */ #include "ssixi6.c" /* Posix on 64-bit Intel stack scan */ @@ -275,7 +275,7 @@ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2012-2016 Ravenbrook Limited . + * Copyright (C) 2012-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/prmcxc.c b/mps/code/prmcxc.c index 315f1d31c9f..61cc3c9ea6e 100644 --- a/mps/code/prmcxc.c +++ b/mps/code/prmcxc.c @@ -1,7 +1,7 @@ -/* prmcxc.c: MUTATOR CONTEXT INTEL 386 (MAC OS X) +/* prmcxc.c: MUTATOR CONTEXT (macOS) * * $Id$ - * Copyright (c) 2016 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2016-2018 Ravenbrook Limited. See end of file for license. * * .purpose: Implement the mutator context module. See . * @@ -88,7 +88,7 @@ Res MutatorContextScan(ScanState ss, MutatorContext context, /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2016 Ravenbrook Limited . + * Copyright (C) 2016-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/prmcxc.h b/mps/code/prmcxc.h index 896fa87313c..30930589acf 100644 --- a/mps/code/prmcxc.h +++ b/mps/code/prmcxc.h @@ -1,7 +1,7 @@ -/* prmcxc.h: MUTATOR CONTEXT FOR OS X MACH +/* prmcxc.h: MUTATOR CONTEXT (macOS) * * $Id$ - * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * .readership: MPS developers. */ @@ -32,7 +32,7 @@ extern void MutatorContextInitThread(MutatorContext context, THREAD_STATE_S *thr /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2016 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/prmcxci3.c b/mps/code/prmcxci3.c index 853b28a70f1..7098ab8e765 100644 --- a/mps/code/prmcxci3.c +++ b/mps/code/prmcxci3.c @@ -1,7 +1,7 @@ -/* prmcxci3.c: MUTATOR CONTEXT INTEL 386 (MAC OS X) +/* prmcxci3.c: MUTATOR CONTEXT (macOS, IA-32) * * $Id$ - * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * .purpose: Implement the mutator context module. See . * @@ -102,7 +102,7 @@ Addr MutatorContextSP(MutatorContext context) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2016 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/prmcxci6.c b/mps/code/prmcxci6.c index f16967b0f85..6a50e73df6c 100644 --- a/mps/code/prmcxci6.c +++ b/mps/code/prmcxci6.c @@ -1,7 +1,7 @@ -/* prmcxci6.c: MUTATOR CONTEXT x64 (OS X) +/* prmcxci6.c: MUTATOR CONTEXT (macOS, x86-64) * * $Id$ - * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * .purpose: Implement the mutator context module. See . * @@ -107,7 +107,7 @@ Addr MutatorContextSP(MutatorContext context) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2016 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/protix.c b/mps/code/protix.c index 43df9630467..51a06402dc6 100644 --- a/mps/code/protix.c +++ b/mps/code/protix.c @@ -1,10 +1,10 @@ /* protix.c: PROTECTION FOR UNIX * * $Id$ - * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * Somewhat generic across different Unix systems. Shared between - * OS X, FreeBSD, and Linux. + * macOS, FreeBSD, and Linux. * * * SOURCES @@ -19,7 +19,7 @@ * be safely passed as a void *. Single UNIX Specification Version 2 (aka * X/OPEN XSH5) says that the parameter is a void *. Some Unix-likes may * declare this parameter as a caddr_t. FreeBSD used to do this (on the now - * very obsolete FreeBSD 2.2.x series), as did OS X, but both now implement + * very obsolete FreeBSD 2.2.x series), as did macOS, but both now implement * it correctly as void *. caddr_t is usually char *. * * .assume.write-only: More of an anti-assumption really. We @@ -119,7 +119,7 @@ Size ProtGranularity(void) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2016 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/protxc.c b/mps/code/protxc.c index 733afcc48c5..f4d51bd409b 100644 --- a/mps/code/protxc.c +++ b/mps/code/protxc.c @@ -1,9 +1,9 @@ -/* protxc.c: PROTECTION EXCEPTION HANDLER FOR OS X MACH +/* protxc.c: PROTECTION EXCEPTION HANDLER (macOS) * * $Id$ - * Copyright (c) 2013-2016 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2013-2018 Ravenbrook Limited. See end of file for license. * - * This is the protection exception handling code for OS X using the + * This is the protection exception handling code for macOS using the * Mach interface (not pthreads). * * In Mach, a thread that hits protected memory is suspended, and a message @@ -15,7 +15,7 @@ * at the next level out (the levels are thread, task, host) by sending a * "fail" reply. * - * In OS X, pthreads are implemented by Mach threads. (The implementation is + * In macOS, pthreads are implemented by Mach threads. (The implementation is * part of the XNU source code at opensource.apple.com. [copy to import?]) So * we can use some pthread interfaces (pthread_create, pthread_once) for * convenience in setting up threads. @@ -47,7 +47,7 @@ * TRANSGRESSIONS * * .trans.stdlib: It's OK to use the C library from here because we know - * we're on OS X and not freestanding. In particular, we use memcpy. + * we're on macOS and not freestanding. In particular, we use memcpy. * * .trans.must: Various OS calls are asserted to succeed, since there isn't * really a dynamic reason they should fail, so it must be a static error. @@ -74,7 +74,7 @@ #include #if !defined(MPS_OS_XC) -#error "protxc.c is OS X specific" +#error "protxc.c is macOS specific" #endif SRCID(protxc, "$Id$"); @@ -178,7 +178,7 @@ static void protMustSend(mach_msg_header_t *head) /* protCatchOne -- catch one EXC_BAD_ACCESS exception message. * - * OS X provides a function exc_server (in + * macOS provides a function exc_server (in * /usr/lib/system/libsystem_kernel.dylib) that's documented in the XNU * sources * and generated by the Mach Interface Generator (mig). It unpacks @@ -437,7 +437,7 @@ void ProtSetup(void) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2013-2016 Ravenbrook Limited . + * Copyright (C) 2013-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/protxc.h b/mps/code/protxc.h index 1e674a3cb20..5ccc8c9cbf6 100644 --- a/mps/code/protxc.h +++ b/mps/code/protxc.h @@ -1,7 +1,7 @@ -/* protxc.h: PROTECTION EXCPETION HANDLER FOR OS X MACH +/* protxc.h: PROTECTION EXCEPTION HANDLER (macOS) * * $Id$ - * Copyright (c) 2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2013-2018 Ravenbrook Limited. See end of file for license. */ #ifndef protxc_h @@ -13,7 +13,7 @@ extern void ProtThreadRegister(void); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2013 Ravenbrook Limited . + * Copyright (C) 2013-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/sc.h b/mps/code/sc.h index da4a9c8eac5..17b6fe74528 100644 --- a/mps/code/sc.h +++ b/mps/code/sc.h @@ -1,7 +1,7 @@ /* sc.h: STACK CONTEXT * * $Id$ - * Copyright (c) 2012 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2012-2018 Ravenbrook Limited. See end of file for license. * * Provides a context to hold the registers and stack pointer * @@ -45,7 +45,7 @@ */ -/* Mac OS X on 32-bit Intel built with Clang or GCC */ +/* macOS on IA-32 built with Clang or GCC */ #if defined(MPS_PF_XCI3LL) || defined(MPS_PF_XCI3GC) @@ -64,7 +64,7 @@ typedef struct StackContextStruct { #define StackContextSP(sc) ((Addr *)(sc)->jumpBuffer[JB_ESP/sizeof(int)]) -/* On MacOS X the stackPointer can end up pointing above the StackContext +/* On macOS the stackPointer can end up pointing above the StackContext * which we assume to be stored on the stack because it is no longer * needed once we have _longjmp()ed back. So take the minimum of the * SP and the base of the StackContext structure. */ @@ -72,7 +72,7 @@ typedef struct StackContextStruct { (StackContextSP(sc) < (Addr*)(sc) ? StackContextSP(sc) : (Addr*)(sc)) -/* Mac OS X on 64-bit Intel build with Clang or GCC */ +/* macOS on x86-64 built with Clang or GCC */ #elif defined(MPS_PF_XCI6LL) || defined(MPS_PF_XCI6GC) @@ -99,7 +99,7 @@ typedef struct StackContextStruct { #define StackContextSP(sc) \ (*(Addr **)((char *)(sc)->jumpBuffer+JB_RSP)) -/* On MacOS X the stackPointer can end up pointing above the StackContext +/* On macOS the stackPointer can end up pointing above the StackContext * which we assume to be stored on the stack because it is no longer * needed once we have _longjmp()ed back. So take the minimum of the * SP and the base of the StackContext structure. */ @@ -165,7 +165,7 @@ typedef struct StackContextStruct { /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2012 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/shield.c b/mps/code/shield.c index 5b3cc818d76..c50218fda3f 100644 --- a/mps/code/shield.c +++ b/mps/code/shield.c @@ -1,7 +1,7 @@ /* shield.c: SHIELD IMPLEMENTATION * * $Id$ - * Copyright (c) 2001-2017 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * See: idea.shield, design.mps.shield. * @@ -381,7 +381,7 @@ static Compare shieldQueueEntryCompare(void *left, void *right, void *closure) * * Sort the shield queue into address order, then iterate over it * coalescing protection work, in order to reduce the number of system - * calls to a minimum. This is very important on OS X, where + * calls to a minimum. This is very important on macOS, where * protection calls are extremely inefficient, but has no net gain on * Windows. * @@ -762,7 +762,7 @@ void (ShieldCover)(Arena arena, Seg seg) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2017 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/ssixi3.c b/mps/code/ssixi3.c index a922cabc5a6..9325b9ae0fa 100644 --- a/mps/code/ssixi3.c +++ b/mps/code/ssixi3.c @@ -1,14 +1,14 @@ -/* ssixi3.c: UNIX/INTEL STACK SCANNING +/* ssixi3.c: STACK SCANNING (POSIX, IA-32) * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * This scans the stack and fixes the registers which may contain * roots. See * * This code was originally developed and tested on Linux, and then - * copied to the FreeBSD and Darwin (OS X) operating systems where it - * also seems to work. Note that on FreeBSD and Darwin it has not + * copied to the FreeBSD and Darwin (macOS) operating systems where + * it also seems to work. Note that on FreeBSD and Darwin it has not * been indepently verified with respect to any ABI documentation. * * This code is common to more than one Unix implementation on @@ -71,7 +71,7 @@ Res StackScan(ScanState ss, Word *stackCold, /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/ssixi6.c b/mps/code/ssixi6.c index 2d70d62697c..ac8dcbd980d 100644 --- a/mps/code/ssixi6.c +++ b/mps/code/ssixi6.c @@ -1,17 +1,17 @@ -/* ssixi6.c: UNIX/x64 STACK SCANNING +/* ssixi6.c: STACK SCANNING (POSIX, x86-64) * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * This scans the stack and fixes the registers which may contain * roots. See * - * This code was branched from ssixi3.c (32-bit Intel) initially for the - * port to XCI6LL (Mac OS X on x86_64 with Clang). + * This code was branched from ssixi3.c (POSIX, IA-32) initially for + * the port to XCI6LL (macOS, x86-64, Clang/LLVM). * - * This code is common to more than one Unix implementation on - * Intel hardware (but is not portable Unix code). According to Wikipedia, - * all the non-Windows platforms use the System V AMD64 ABI. See + * This code is common to more than one Unix implementation on Intel + * hardware (but is not portable Unix code). According to Wikipedia, + * all the non-Windows platforms use the System V AMD64 ABI. See * .sources.callees.saves. * * SOURCES @@ -71,7 +71,7 @@ Res StackScan(ScanState ss, Word *stackCold, /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/thxc.c b/mps/code/thxc.c index 403a35a2883..88b1af7ca61 100644 --- a/mps/code/thxc.c +++ b/mps/code/thxc.c @@ -1,7 +1,7 @@ -/* thxc.c: OS X MACH THREADS MANAGER +/* thxc.c: THREAD MANAGER (macOS) * * $Id$ - * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * .design: See . * @@ -32,7 +32,7 @@ SRCID(thxc, "$Id$"); -typedef struct mps_thr_s { /* OS X / Mach thread structure */ +typedef struct mps_thr_s { /* macOS thread structure */ Sig sig; /* */ Serial serial; /* from arena->threadSerial */ Arena arena; /* owning arena */ @@ -388,7 +388,7 @@ void ThreadSetup(void) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2016 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/vmix.c b/mps/code/vmix.c index 61e093a02e8..11782140764 100644 --- a/mps/code/vmix.c +++ b/mps/code/vmix.c @@ -1,7 +1,7 @@ -/* vmix.c: VIRTUAL MEMORY MAPPING FOR UNIX (ISH) +/* vmix.c: VIRTUAL MEMORY MAPPING (POSIX) * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * .purpose: This is the implementation of the virtual memory mapping * interface (vm.h) for Unix-like operating systems. It was created @@ -10,7 +10,7 @@ * copied from vli.c (Linux) which was itself copied from vmo1.c (OSF/1 * / DIGITAL UNIX / Tru64). * - * .deployed: Currently used on Darwin (OS X) and FreeBSD. + * .deployed: Currently used on Darwin (macOS) and FreeBSD. * * .design: See . .design.mmap: mmap(2) is used to * reserve address space by creating a mapping with page access none. @@ -225,7 +225,7 @@ void VMUnmap(VM vm, Addr base, Addr limit) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/xci3gc.gmk b/mps/code/xci3gc.gmk index c20e7f43598..d7276b79647 100644 --- a/mps/code/xci3gc.gmk +++ b/mps/code/xci3gc.gmk @@ -1,9 +1,9 @@ # -*- makefile -*- # -# xci3gc.gmk: BUILD FOR MACOS X (CARBON)/INTEL IA32/GCC PLATFORM +# xci3gc.gmk: BUILD FOR macOS/IA-32/GCC PLATFORM # # $Id$ -# Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. # # Naively copied from xcppgc.gmk, could do with going over properly. @@ -34,7 +34,7 @@ include comm.gmk # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2016 Ravenbrook Limited . +# Copyright (C) 2001-2018 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/code/xci3ll.gmk b/mps/code/xci3ll.gmk index 9cabd39de23..c6d874aa194 100644 --- a/mps/code/xci3ll.gmk +++ b/mps/code/xci3ll.gmk @@ -1,9 +1,9 @@ # -*- makefile -*- # -# xci3ll.gmk: BUILD FOR MAC OS X/i386/Clang PLATFORM +# xci3ll.gmk: BUILD FOR macOS/IA-32/Clang/LLVM PLATFORM # # $Id$ -# Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. # # .prefer.xcode: The documented and preferred way to develop the MPS # for this platform is to use the Xcode project (mps.xcodeproj). This @@ -34,7 +34,7 @@ include comm.gmk # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2016 Ravenbrook Limited . +# Copyright (C) 2001-2018 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/code/xci6gc.gmk b/mps/code/xci6gc.gmk index 3ff0d778557..cf13ebb09d1 100644 --- a/mps/code/xci6gc.gmk +++ b/mps/code/xci6gc.gmk @@ -1,9 +1,9 @@ # -*- makefile -*- # -# xci6gc.gmk: BUILD FOR MAC OS X/x86_64/GCC PLATFORM +# xci6gc.gmk: BUILD FOR macOS/x86-64/GCC PLATFORM # # $Id$ -# Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. # # .prefer.xcode: The documented and preferred way to develop the MPS # for this platform is to use the Xcode project (mps.xcodeproj). This @@ -31,7 +31,7 @@ include comm.gmk # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2016 Ravenbrook Limited . +# Copyright (C) 2001-2018 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/code/xci6ll.gmk b/mps/code/xci6ll.gmk index fe10bab67bd..6c5b5b35454 100644 --- a/mps/code/xci6ll.gmk +++ b/mps/code/xci6ll.gmk @@ -1,9 +1,9 @@ # -*- makefile -*- # -# xci6ll.gmk: BUILD FOR MAC OS X/x86_64/Clang PLATFORM +# xci6ll.gmk: BUILD FOR macOS/x86-64/Clang/LLVM PLATFORM # # $Id$ -# Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. # # .prefer.xcode: The documented and preferred way to develop the MPS # for this platform is to use the Xcode project (mps.xcodeproj). This @@ -31,7 +31,7 @@ include comm.gmk # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2016 Ravenbrook Limited . +# Copyright (C) 2001-2018 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/design/prmc.txt b/mps/design/prmc.txt index ceea30a6ff4..06d91662d7e 100644 --- a/mps/design/prmc.txt +++ b/mps/design/prmc.txt @@ -263,8 +263,8 @@ _`.impl.w3.context.sp`: The stack pointer is obtained from ``CONTEXT.Esp`` (on IA-32) or ``CONTEXT.Rsp`` (on x86-64). -OS X implementation -................... +macOS implementation +.................... _`.impl.xc`: In ``prmcix.c`` and ``prmcxc.c``, with processor-specific parts in ``prmci3.c`` and ``prmci6.c``, and other platform-specific @@ -311,7 +311,7 @@ Document History Copyright and License --------------------- -Copyright © 2014-2016 Ravenbrook Limited . +Copyright © 2014-2018 Ravenbrook Limited . All rights reserved. This is an open source license. Contact Ravenbrook for commercial licensing options. diff --git a/mps/design/prot.txt b/mps/design/prot.txt index 5f04ce94c54..1a7cc26a091 100644 --- a/mps/design/prot.txt +++ b/mps/design/prot.txt @@ -139,7 +139,7 @@ _`.impl.ix`: POSIX implementation. See design.mps.protix_. _`.impl.w3`: Windows implementation. -_`.impl.xc`: OS X implementation. +_`.impl.xc`: macOS implementation. Document History @@ -163,7 +163,7 @@ Document History Copyright and License --------------------- -Copyright © 2013-2016 Ravenbrook Limited . +Copyright © 2013-2018 Ravenbrook Limited . All rights reserved. This is an open source license. Contact Ravenbrook for commercial licensing options. diff --git a/mps/design/shield.txt b/mps/design/shield.txt index 320971fd211..9a82c7eead3 100644 --- a/mps/design/shield.txt +++ b/mps/design/shield.txt @@ -156,7 +156,7 @@ This hysteresis allows the MPS to proceed with garbage collection during a pause without actually setting hardware protection until it returns to the mutator. This is particularly important on operating systems where the protection is expensive and poorly implemented, such -as OS X. +as macOS. The queue also ensures that no memory protection system calls will be needed for incremental garbage collection if a complete collection @@ -362,7 +362,7 @@ Theoretically we can do this, but: non-moving collection. 2. The main cost of protection is changing it at all, not whether we - change just read or write. On OS X, the main cost seems to be the + change just read or write. On macOS, the main cost seems to be the TLB flush, which affects wall-clock time of everything on the processor! @@ -406,7 +406,7 @@ Document History Copyright and License --------------------- -Copyright © 2013-2017 Ravenbrook Limited . +Copyright © 2013-2018 Ravenbrook Limited . All rights reserved. This is an open source license. Contact Ravenbrook for commercial licensing options. diff --git a/mps/design/thread-manager.txt b/mps/design/thread-manager.txt index 57060e5266e..2ecba250803 100644 --- a/mps/design/thread-manager.txt +++ b/mps/design/thread-manager.txt @@ -279,8 +279,8 @@ _`.impl.w3.scan.suspended`: Otherwise, ``ThreadScan()`` calls pointer. -OS X implementation -................... +macOS implementation +.................... _`.impl.xc`: In ``thxc.c``. @@ -335,7 +335,7 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited . +Copyright © 2013-2018 Ravenbrook Limited . All rights reserved. This is an open source license. Contact Ravenbrook for commercial licensing options. diff --git a/mps/design/vm.txt b/mps/design/vm.txt index ab17a85f4e1..ef65ffffd0c 100644 --- a/mps/design/vm.txt +++ b/mps/design/vm.txt @@ -270,7 +270,7 @@ passing ``PROT_NONE`` and ``MAP_PRIVATE | MAP_ANON``. _`.impl.ix.anon.trans`: Note that ``MAP_ANON`` ("map anonymous memory not associated with any specific file") is an extension to -POSIX, but it is supported by FreeBSD, Linux, and OS X. A work-around +POSIX, but it is supported by FreeBSD, Linux, and macOS. A work-around that was formerly used on systems lacking ``MAP_ANON`` was to map the file ``/dev/zero``. @@ -369,7 +369,7 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited . +Copyright © 2013-2018 Ravenbrook Limited . All rights reserved. This is an open source license. Contact Ravenbrook for commercial licensing options. diff --git a/mps/design/write-barrier.txt b/mps/design/write-barrier.txt index 3b54c882964..372215c23be 100644 --- a/mps/design/write-barrier.txt +++ b/mps/design/write-barrier.txt @@ -105,7 +105,7 @@ Improvements .improv.by-os: The overheads hardware barriers varies widely between operating systems. On Windows it is very cheap to change memory -protection and to handle protecion faults. On OS X it is very +protection and to handle protecion faults. On macOS it is very expensive. The balance between barriers and scanning work is different. We should measure the relative costs and tune the deferral for each separately. @@ -138,7 +138,7 @@ Document History Copyright and License --------------------- -Copyright © 2016 Ravenbrook Limited . All +Copyright © 2016-2018 Ravenbrook Limited . All rights reserved. This is an open source license. Contact Ravenbrook for commercial licensing options. diff --git a/mps/manual/build.txt b/mps/manual/build.txt index c2aa184997f..357fb14a49e 100644 --- a/mps/manual/build.txt +++ b/mps/manual/build.txt @@ -34,14 +34,14 @@ Compiling for production In the simplest case, you can compile the MPS to an object file with just:: - cc -c mps.c (Unix/Mac OS X) + cc -c mps.c (Unix/macOS) cl /c mps.c (Windows) This will build a "hot" variety (for production) object file for use with ``mps.h``. You can greatly improve performance by allowing global optimization, for example:: - cc -O2 -c mps.c (Unix/Mac OS X) + cc -O2 -c mps.c (Unix/macOS) cl /O2 /c mps.c (Windows) @@ -51,7 +51,7 @@ Compiling for debugging You can get a "cool" variety MPS (with more internal checking, for debugging and development) with:: - cc -g -DCONFIG_VAR_COOL -c mps.c (Unix/Mac OS X) + cc -g -DCONFIG_VAR_COOL -c mps.c (Unix/macOS) cl /Zi /DCONFIG_VAR_COOL /c mps.c (Windows) @@ -68,7 +68,7 @@ between it and the MPS. So if your format implementation is in, say, then:: - cc -O2 -c mymps.c (Unix/Mac OS X) + cc -O2 -c mymps.c (Unix/macOS) cl /O2 /c mymps.c (Windows) This will get your format code inlined with the MPS garbage collector. @@ -111,7 +111,7 @@ with ``pkg_add -r gmake``. On Windows platforms the NMAKE tool is used. This comes with Microsoft Visual Studio C++ or the Microsoft Windows SDK. -On Mac OS X the MPS is built using Xcode, either by opening +On macOS the MPS is built using Xcode, either by opening ``mps.xcodeproj`` with the Xcode app, or using the command-line "xcodebuild" tool, installed from Xcode → Preferences → Downloads → Components → Command Line Tools. @@ -137,15 +137,15 @@ Platform OS Architecture Compiler Makefile ========== ========= ============= ============ ================= ``fri3gc`` FreeBSD IA-32 GCC ``fri3gc.gmk`` ``fri3ll`` FreeBSD IA-32 Clang ``fri3ll.gmk`` -``fri6gc`` FreeBSD x86_64 GCC ``fri6gc.gmk`` -``fri6ll`` FreeBSD x86_64 Clang ``fri6ll.gmk`` +``fri6gc`` FreeBSD x86-64 GCC ``fri6gc.gmk`` +``fri6ll`` FreeBSD x86-64 Clang ``fri6ll.gmk`` ``lii3gc`` Linux IA-32 GCC ``lii3gc.gmk`` -``lii6gc`` Linux x86_64 GCC ``lii6gc.gmk`` -``lii6ll`` Linux x86_64 Clang ``lii6ll.gmk`` +``lii6gc`` Linux x86-64 GCC ``lii6gc.gmk`` +``lii6ll`` Linux x86-64 Clang ``lii6ll.gmk`` ``w3i3mv`` Windows IA-32 Microsoft C ``w3i3mv.nmk`` -``w3i6mv`` Windows x86_64 Microsoft C ``w3i6mv.nmk`` -``xci3ll`` Mac OS X IA-32 Clang ``mps.xcodeproj`` -``xci6ll`` Mac OS X x86_64 Clang ``mps.xcodeproj`` +``w3i6mv`` Windows x86-64 Microsoft C ``w3i6mv.nmk`` +``xci3ll`` macOS IA-32 Clang ``mps.xcodeproj`` +``xci6ll`` macOS x86-64 Clang ``mps.xcodeproj`` ========== ========= ============= ============ ================= Historically, the MPS worked on a much wider variety of platforms, and @@ -195,13 +195,13 @@ To build just one target, run one of these commands:: nmake /f w3i3mv.nmk (32-bit) nmake /f w3i6mv.nmk (64-bit) -On Mac OS X, you can build from the command line with:: +On macOS, you can build from the command line with:: xcodebuild On most platforms, the output of the build goes to a directory named after the platform (e.g. ``lii6ll``) so that you can share the source -tree across platforms. On Mac OS X the output goes in a directory +tree across platforms. On macOS the output goes in a directory called ``xc``. Building generates ``mps.a`` or ``mps.lib`` or equivalent, a library of object code which you can link with your application, subject to the :ref:`MPS licensing conditions `. @@ -241,7 +241,7 @@ loads a diagnostic stream of events into a `SQLite3 `_ database for processing. In order to build this program, you need to install the SQLite3 development resources. -* On Mac OS X, SQLite3 is pre-installed, so this tool builds by +* On macOS, SQLite3 is pre-installed, so this tool builds by default. * On Linux, you need to install the ``libsqlite3-dev`` package:: diff --git a/mps/manual/source/code-index.rst b/mps/manual/source/code-index.rst index 523b42d7113..fe0991f9d79 100644 --- a/mps/manual/source/code-index.rst +++ b/mps/manual/source/code-index.rst @@ -180,17 +180,17 @@ prmcw3.c Mutator context implementation for Windows. prmcw3.h Mutator context interface for Windows. prmcw3i3.c Mutator context implementation for Windows, IA-32. prmcw3i6.c Mutator context implementation for Windows, x86-64. -prmcxc.c Mutator context implementation for OS X. -prmcxc.h Mutator context interface for OS X. -prmcxci3.c Mutator context implementation for OS X, IA-32. -prmcxci6.c Mutator context implementation for OS X, x86-64. +prmcxc.c Mutator context implementation for macOS. +prmcxc.h Mutator context interface for macOS. +prmcxci3.c Mutator context implementation for macOS, IA-32. +prmcxci6.c Mutator context implementation for macOS, x86-64. prot.h Protection interface. See design.mps.prot_. protan.c Protection implementation for standard C. protix.c Protection implementation for POSIX. protsgix.c Protection implementation for POSIX (signals part). protw3.c Protection implementation for Windows. -protxc.c Protection implementation for OS X. -protxc.h Protection interface for OS X. +protxc.c Protection implementation for macOS. +protxc.h Protection interface for macOS. pthrdext.c Protection implementation for POSIX (threads part). pthrdext.h Protection interface for POSIX (threads part). sp.h Stack probe interface. See design.mps.sp_. @@ -210,7 +210,7 @@ th.h Threads interface. See design.mps.thread-manager_. than.c Threads implementation for standard C. thix.c Threads implementation for POSIX. thw3.c Threads implementation for Windows. -thxc.c Threads implementation for OS X. +thxc.c Threads implementation for macOS. vm.c Virtual memory implementation (common part). vm.h Virtual memory interface. See design.mps.vm_. vman.c Virtual memory implementation for standard C. @@ -349,6 +349,7 @@ expt825.c Regression test for job000825_. fbmtest.c Free block manager (CBS and Freelist) test. finalcv.c :ref:`topic-finalization` coverage test. finaltest.c :ref:`topic-finalization` test. +forktest.c :ref:`topic-fork-safety` test. fotest.c Failover allocator test. landtest.c Land test. locbwcss.c Locus backwards compatibility stress test. diff --git a/mps/manual/source/glossary/m.rst b/mps/manual/source/glossary/m.rst index 92abdcf2740..e078367fbd0 100644 --- a/mps/manual/source/glossary/m.rst +++ b/mps/manual/source/glossary/m.rst @@ -30,8 +30,8 @@ Memory Management Glossary: M but faster and more expensive than :term:`backing store`. It is common to refer only to the main memory of a computer; - for example, "This server has 128 GB of memory" and "OS X 10.8 - requires at least 2 GB of memory". + for example, "This server has 128 GB of memory" and "macOS + High Sierra requires at least 2 GB of memory". .. historical:: diff --git a/mps/manual/source/guide/debug.rst b/mps/manual/source/guide/debug.rst index a922ad35af7..76e0bb8f42a 100644 --- a/mps/manual/source/guide/debug.rst +++ b/mps/manual/source/guide/debug.rst @@ -91,7 +91,7 @@ General debugging advice On these operating systems, you can add this command to your ``.gdbinit`` if you always want it to be run. - On OS X, barrier hits do not use signals and so do not enter the + On macOS, barrier hits do not use signals and so do not enter the debugger. #. .. index:: @@ -155,7 +155,7 @@ program (data segment, text segment, stack and heap): } When ASLR is turned on, running this program outputs different -addresses on each run. For example, here are four runs on OS X +addresses on each run. For example, here are four runs on macOS 10.9.3:: data: 0x10a532020 text: 0x10a531ed0 stack: 0x7fff556ceb1c heap: 0x7f9f80c03980 @@ -196,7 +196,7 @@ Here's the situation on each of the operating systems supported by the MPS: $ setarch $(uname -m) -R ./myprogram -* On **OS X** (10.7 or later), ASLR can be disabled for a single +* On **macOS** (10.7 or later), ASLR can be disabled for a single process by starting the process using :c:func:`posix_spawn`, passing the undocumented attribute ``0x100``, like this: @@ -213,7 +213,7 @@ Here's the situation on each of the operating systems supported by the MPS: The MPS provides the source code for a command-line tool implementing this (``tool/noaslr.c``). We've confirmed that this - works on OS X 10.9.3, but since the technique is undocumented, it + works on macOS 10.9.3, but since the technique is undocumented, it may well break in future releases. (If you know of a documented way to achieve this, please :ref:`contact us `.) diff --git a/mps/manual/source/guide/overview.rst b/mps/manual/source/guide/overview.rst index dc7652af1c9..ad5389f7af9 100644 --- a/mps/manual/source/guide/overview.rst +++ b/mps/manual/source/guide/overview.rst @@ -55,7 +55,7 @@ The MPS is currently supported for deployment on: - FreeBSD 7 or later, on IA-32 and x86-64, using GCC or Clang/LLVM; -- OS X 10.4 or later, on IA-32 and x86-64, using Clang/LLVM. +- macOS 10.4 or later, on IA-32 and x86-64, using Clang/LLVM. The MPS is highly portable and has run on many other processors and operating systems in the past (see :ref:`guide-build`). Most of the diff --git a/mps/manual/source/pool/awl.rst b/mps/manual/source/pool/awl.rst index ec621394774..4ff510e578a 100644 --- a/mps/manual/source/pool/awl.rst +++ b/mps/manual/source/pool/awl.rst @@ -252,7 +252,7 @@ following are true: #. The MPS is running on Linux/IA-32 or Windows/IA-32. Extending this list to new (reasonable) operating systems should be tolerable (for - example, OS X/IA-32). Extending this to new processor architectures + example, macOS/IA-32). Extending this to new processor architectures requires more work. #. The processor instruction that is accessing the object is of a diff --git a/mps/manual/source/topic/platform.rst b/mps/manual/source/topic/platform.rst index b075ea71e94..0538ec52f16 100644 --- a/mps/manual/source/topic/platform.rst +++ b/mps/manual/source/topic/platform.rst @@ -20,7 +20,7 @@ six-character code breaks down into three pairs of characters: ``fr`` FreeBSD :c:macro:`MPS_OS_FR` ``li`` Linux :c:macro:`MPS_OS_LI` ``w3`` Windows :c:macro:`MPS_OS_W3` -``xc`` OS X :c:macro:`MPS_OS_XC` +``xc`` macOS :c:macro:`MPS_OS_XC` ====== ================ ==================== The second pair of characters names the processor architecture: @@ -126,7 +126,7 @@ Platform interface .. c:macro:: MPS_OS_XC A :term:`C` preprocessor macro that indicates, if defined, that - the MPS was compiled on an OS X operating system. + the MPS was compiled on an macOS operating system. .. c:macro:: MPS_PF_ALIGN @@ -209,28 +209,28 @@ Platform interface .. c:macro:: MPS_PF_XCI3GC A :term:`C` preprocessor macro that indicates, if defined, that - the :term:`platform` consists of the OS X operating system, the + the :term:`platform` consists of the macOS operating system, the IA-32 processor architecture, and the GCC compiler. .. c:macro:: MPS_PF_XCI3LL A :term:`C` preprocessor macro that indicates, if defined, that - the :term:`platform` consists of the OS X operating system, the + the :term:`platform` consists of the macOS operating system, the IA-32 processor architecture, and the Clang/LLVM compiler. .. c:macro:: MPS_PF_XCI6GC A :term:`C` preprocessor macro that indicates, if defined, that - the :term:`platform` consists of the OS X operating system, the + the :term:`platform` consists of the macOS operating system, the x86-64 processor architecture, and the GCC compiler. .. c:macro:: MPS_PF_XCI6LL A :term:`C` preprocessor macro that indicates, if defined, that - the :term:`platform` consists of the OS X operating system, the + the :term:`platform` consists of the macOS operating system, the x86-64 processor architecture, and the Clang/LLVM compiler. diff --git a/mps/manual/source/topic/plinth.rst b/mps/manual/source/topic/plinth.rst index e138cb60347..780e12be7e6 100644 --- a/mps/manual/source/topic/plinth.rst +++ b/mps/manual/source/topic/plinth.rst @@ -72,7 +72,7 @@ for useful advice.) If this preprocessor constant is defined, exclude the ANSI plinth (``mpsioan.c`` and ``mpsliban.c``) from the MPS. For example:: - cc -DCONFIG_PLINTH_NONE -c mps.c (Unix/OS X) + cc -DCONFIG_PLINTH_NONE -c mps.c (Unix/macOS) cl /Gs /DCONFIG_PLINTH_NONE /c mps.c (Windows) Having excluded the ANSI plinth, you must of course supply your diff --git a/mps/manual/source/topic/porting.rst b/mps/manual/source/topic/porting.rst index aa2fe5de03d..ecbd01ffb76 100644 --- a/mps/manual/source/topic/porting.rst +++ b/mps/manual/source/topic/porting.rst @@ -61,7 +61,7 @@ usable. See :ref:`design-prot` for the design, and ``prot.h`` for the interface. There are implementations for POSIX in ``protix.c`` plus - ``protsgix.c``, Windows in ``protw3.c``, and OS X using Mach in + ``protsgix.c``, Windows in ``protw3.c``, and macOS using Mach in ``protix.c`` plus ``protxc.c``. There is a generic implementation in ``protan.c``, which can't @@ -76,7 +76,7 @@ usable. stack` can be scanned. See :ref:`design-prmc` for the design, and ``prmc.h`` for the - interface. There are implementations on Unix, Windows, and OS X for + interface. There are implementations on Unix, Windows, and macOS for IA-32 and x86-64. There is a generic implementation in ``prmcan.c``, which can't @@ -117,7 +117,7 @@ usable. See :ref:`design-thread-manager` for the design, and ``th.h`` for the interface. There are implementations for POSIX in ``thix.c`` - plus ``pthrdext.c``, OS X using Mach in ``thxc.c``, Windows in + plus ``pthrdext.c``, macOS using Mach in ``thxc.c``, Windows in ``thw3.c``. There is a generic implementation in ``than.c``, which necessarily diff --git a/mps/manual/source/topic/security.rst b/mps/manual/source/topic/security.rst index 780c4c58ff1..16a148b2a8b 100644 --- a/mps/manual/source/topic/security.rst +++ b/mps/manual/source/topic/security.rst @@ -34,7 +34,7 @@ determine the address of allocated structures. There is currently no workaround for this issue. If this affects you, please :ref:`contact us `. -Other supported platforms are unaffected by this issue: Linux and OS X +Other supported platforms are unaffected by this issue: Linux and macOS randomize the addresses allocated by :c:func:`mmap`, and Windows randomizes the addresses allocated by :c:func:`VirtualAlloc`. diff --git a/mps/manual/source/topic/thread.rst b/mps/manual/source/topic/thread.rst index a52254382fc..056a9dbce34 100644 --- a/mps/manual/source/topic/thread.rst +++ b/mps/manual/source/topic/thread.rst @@ -104,7 +104,7 @@ Signal and exception handling issues * On Windows, you must not install a first-chance exception handler. - * On OS X, you must not install a thread-local Mach exception handler + * On macOS, you must not install a thread-local Mach exception handler for ``EXC_BAD_ACCESS`` exceptions. All of these things are, in fact, possible, but your program must diff --git a/mps/procedure/release-build.rst b/mps/procedure/release-build.rst index 2485c6140a7..d07d4b95ad8 100644 --- a/mps/procedure/release-build.rst +++ b/mps/procedure/release-build.rst @@ -146,7 +146,7 @@ the branch. A typical invocation looks like this:: release name according to the variant, for example, ``mps-cet-1.110.0.zip`` -On a Unix (including OS X) machine: +On a Unix (including macOS) machine: #. Create a fresh Perforce client workspace:: diff --git a/mps/readme.txt b/mps/readme.txt index f5c8c24a76c..747abcc727c 100644 --- a/mps/readme.txt +++ b/mps/readme.txt @@ -53,7 +53,7 @@ using it. The basic case is straightforward on supported platforms (see below):: cd code - cc -O2 -c mps.c Unix / Mac OS X (with Xcode command line) + cc -O2 -c mps.c Unix / macOS (with Xcode command line) cl /O2 /c mps.c Windows (with Microsoft SDK or Visual Studio 2010) This will produce an object file you can link with your project. For @@ -79,7 +79,7 @@ The MPS is currently supported for deployment on: - FreeBSD 7 or later, on IA-32 and x86-64, using GCC or Clang/LLVM; -- OS X 10.4 or later, on IA-32 and x86-64, using Clang/LLVM. +- macOS 10.4 or later, on IA-32 and x86-64, using Clang/LLVM. The MPS is highly portable and has run on many other processors and operating systems in the past (see `Building the MPS @@ -144,7 +144,7 @@ Document History Copyright and Licence --------------------- -Copyright (C) 2001-2016 Ravenbrook Limited. All rights reserved. +Copyright (C) 2001-2018 Ravenbrook Limited. All rights reserved. . This is an open source license. Contact Ravenbrook for commercial licensing options. diff --git a/mps/test/test/script/platform b/mps/test/test/script/platform index 75d9f18d3ca..29c1f23c562 100644 --- a/mps/test/test/script/platform +++ b/mps/test/test/script/platform @@ -13,7 +13,7 @@ # Set lots of variables correctly, depending on the platform # (which was determined in 'options') # -# Currently, it should work correctly on Windows, Linux, MacOS X, +# Currently, it should work correctly on Windows, Linux, macOS, # FreeBSD. # diff --git a/mps/test/test/script/version b/mps/test/test/script/version index 7a49f65b1ca..c2389cae686 100644 --- a/mps/test/test/script/version +++ b/mps/test/test/script/version @@ -33,4 +33,4 @@ $HARNESS_VERSION="3.5"; # 3.3.1: fix bug in reporting compiler errors when compilation # _succeeds_ # 3.4 -- Added P= (pathname equality) operator -# 3.5 -- Platform detection based on uname; Linux and Mac OS X stuff +# 3.5 -- Platform detection based on uname; Linux and macOS stuff diff --git a/mps/tool/index.rst b/mps/tool/index.rst index 8edc44095af..178d88347dc 100644 --- a/mps/tool/index.rst +++ b/mps/tool/index.rst @@ -37,8 +37,8 @@ This document is not confidential. `testrun.bat`_ Implements the ``testrun`` make target on Windows, where it is invoked from ``commpost.nmk``. `testrun.sh`_ Implements the ``testrun`` make target on FreeBSD and - Linux, it is invoked from ``comm.gmk``, and on OS X, where - it is invoked from the Xcode project. + Linux, it is invoked from ``comm.gmk``, and on macOS, + where it is invoked from the Xcode project. ================= ========================================================== .. _branch: branch @@ -75,7 +75,7 @@ B. Document History C. Copyright and License ------------------------ -Copyright © 2002-2014 Ravenbrook Limited. All rights reserved. +Copyright © 2002-2018 Ravenbrook Limited. All rights reserved. This is an open source license. Contact Ravenbrook for commercial licensing options. From 1ad6c163c00515d5cfe6545ed6c65194fdb7b135 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Fri, 15 Jun 2018 12:42:49 +0100 Subject: [PATCH 25/27] Cross-reference from release notes to supported platforms. Copied from Perforce Change: 193827 --- mps/manual/source/code-index.rst | 2 +- mps/manual/source/guide/overview.rst | 2 ++ mps/manual/source/release.rst | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/mps/manual/source/code-index.rst b/mps/manual/source/code-index.rst index fe0991f9d79..731072601b4 100644 --- a/mps/manual/source/code-index.rst +++ b/mps/manual/source/code-index.rst @@ -349,7 +349,7 @@ expt825.c Regression test for job000825_. fbmtest.c Free block manager (CBS and Freelist) test. finalcv.c :ref:`topic-finalization` coverage test. finaltest.c :ref:`topic-finalization` test. -forktest.c :ref:`topic-fork-safety` test. +forktest.c :ref:`topic-thread-fork` test. fotest.c Failover allocator test. landtest.c Land test. locbwcss.c Locus backwards compatibility stress test. diff --git a/mps/manual/source/guide/overview.rst b/mps/manual/source/guide/overview.rst index ad5389f7af9..116843e9697 100644 --- a/mps/manual/source/guide/overview.rst +++ b/mps/manual/source/guide/overview.rst @@ -43,6 +43,8 @@ for details. single: Memory Pool System; supported target platforms single: platforms; supported +.. _guide-overview-platforms: + Supported target platforms -------------------------- diff --git a/mps/manual/source/release.rst b/mps/manual/source/release.rst index c1adfeb44c2..bdde9d1e04d 100644 --- a/mps/manual/source/release.rst +++ b/mps/manual/source/release.rst @@ -33,7 +33,8 @@ New features #. The MPS no longer supports Linux 2.4 and 2.5. (These versions used LinuxThreads_ instead of POSIX threads; all major distributions have long since ceased to support these versions and so it is no - longer convenient to test against them.) + longer convenient to test against them.) See + :ref:`guide-overview-platforms`. .. _LinuxThreads: http://pauillac.inria.fr/~xleroy/linuxthreads/ From e9d454d79604af75b87b2a08ed7a80e76f86b94c Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Fri, 15 Jun 2018 12:44:51 +0100 Subject: [PATCH 26/27] It's safe to register a thread multiple times on macos, so there is no need for the guard. Copied from Perforce Change: 193828 --- mps/code/protxc.c | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/mps/code/protxc.c b/mps/code/protxc.c index f4d51bd409b..6b8b48776a3 100644 --- a/mps/code/protxc.c +++ b/mps/code/protxc.c @@ -286,17 +286,6 @@ static void *protCatchThread(void *p) { } -/* protSetupThread -- the Mach port number of either the very first - * thread to create an arena, or else the child thread after a fork. - * - * The purpose is to avoid registering this thread twice if the client - * program calls mps_thread_reg on it. We need this special case - * because we don't require thread registration of the sole thread of - * a single-threaded mutator. - */ -static mach_port_t protSetupThread = MACH_PORT_NULL; - - /* ProtThreadRegister -- register a thread for protection exception handling */ extern void ProtThreadRegister(void) @@ -312,11 +301,7 @@ extern void ProtThreadRegister(void) self = mach_thread_self(); AVER(MACH_PORT_VALID(self)); - - /* Avoid setting up the exception handler twice. */ - if (self == protSetupThread) - return; - + /* Ask to receive EXC_BAD_ACCESS exceptions on our port, complete with thread state and identity information in the message. The MACH_EXCEPTION_CODES flag causes the code fields to be @@ -377,9 +362,9 @@ static void protExcThreadStart(void) if (kr != KERN_SUCCESS) mach_error("ERROR: MPS mach_port_insert_right", kr); /* .trans.must */ - protSetupThread = MACH_PORT_NULL; + /* We don't require the mutator to register the sole thread in a + * single-threaded program, so register it automatically now. */ ProtThreadRegister(); - protSetupThread = self; /* Launch the exception handling thread. We use pthread_create * because it's much simpler than setting up a thread from scratch @@ -414,7 +399,6 @@ static void protAtForkChild(void) static void protSetupInner(void) { - AVER(protSetupThread == MACH_PORT_NULL); protExcThreadStart(); /* Install fork handlers . */ From f5ffaeb349b9d17e9a6b5bf13357b504c75cdb9d Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Fri, 15 Jun 2018 12:54:28 +0100 Subject: [PATCH 27/27] Mach_thread_self() can in theory "return mach_port_null if a resource shortage prevented the reception of the send right" so add a check in each case. (there's nothing we can do about it but at least we can notice.) Copied from Perforce Change: 193831 --- mps/code/thxc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mps/code/thxc.c b/mps/code/thxc.c index 88b1af7ca61..f15175f2399 100644 --- a/mps/code/thxc.c +++ b/mps/code/thxc.c @@ -85,6 +85,7 @@ Res ThreadRegister(Thread *threadReturn, Arena arena) thread->alive = TRUE; thread->forking = FALSE; thread->port = mach_thread_self(); + AVER(MACH_PORT_VALID(thread->port)); thread->sig = ThreadSig; AVERT(Thread, thread); @@ -309,9 +310,12 @@ Res ThreadDescribe(Thread thread, mps_lib_FILE *stream, Count depth) static Bool threadForkPrepare(Thread thread) { + mach_port_t self; AVERT(Thread, thread); AVER(!thread->forking); - thread->forking = (thread->port == mach_thread_self()); + self = mach_thread_self(); + AVER(MACH_PORT_VALID(self)); + thread->forking = (thread->port == self); return TRUE; }