From 8dd16259287f58f9273002717ec4d27e97127719 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 12 Jun 2024 07:43:14 +0200 Subject: Merging upstream version 127.0. Signed-off-by: Daniel Baumann --- ipc/ipdl/Makefile.in | 2 + ipc/ipdl/ipdl.py | 585 ++++++++++++++----------- ipc/ipdl/ipdl/__init__.py | 2 +- ipc/ipdl/ipdl/lower.py | 179 +++----- ipc/ipdl/test/gtest/PTestDestroyNested.ipdl | 18 + ipc/ipdl/test/gtest/PTestDestroyNestedSub.ipdl | 19 + ipc/ipdl/test/gtest/TestDestroyNested.cpp | 123 ++++++ ipc/ipdl/test/gtest/moz.build | 3 + 8 files changed, 548 insertions(+), 383 deletions(-) create mode 100644 ipc/ipdl/test/gtest/PTestDestroyNested.ipdl create mode 100644 ipc/ipdl/test/gtest/PTestDestroyNestedSub.ipdl create mode 100644 ipc/ipdl/test/gtest/TestDestroyNested.cpp (limited to 'ipc/ipdl') diff --git a/ipc/ipdl/Makefile.in b/ipc/ipdl/Makefile.in index 6ebff9cba0..4847569a56 100644 --- a/ipc/ipdl/Makefile.in +++ b/ipc/ipdl/Makefile.in @@ -19,6 +19,7 @@ ipdl_py_deps := \ # NB: the IPDL compiler manages .ipdl-->.h/.cpp dependencies itself, # which is why we don't have explicit .h/.cpp targets here ipdl.track: $(ALL_IPDLSRCS) $(ALL_IPDLSRCS_FILE) $(srcdir)/sync-messages.ini $(srcdir)/message-metadata.ini $(ipdl_py_deps) + $(call BUILDSTATUS,START_Ipdl ipdl.py) $(PYTHON3) $(srcdir)/ipdl.py \ --sync-msg-list=$(srcdir)/sync-messages.ini \ --msg-metadata=$(srcdir)/message-metadata.ini \ @@ -27,6 +28,7 @@ ipdl.track: $(ALL_IPDLSRCS) $(ALL_IPDLSRCS_FILE) $(srcdir)/sync-messages.ini $(s $(IPDLDIRS:%=-I%) \ --file-list $(ALL_IPDLSRCS_FILE) touch $@ + $(call BUILDSTATUS,END_Ipdl ipdl.py) export:: ipdl.track endif diff --git a/ipc/ipdl/ipdl.py b/ipc/ipdl/ipdl.py index 8e5dd5db3d..5c4b7a8344 100644 --- a/ipc/ipdl/ipdl.py +++ b/ipc/ipdl/ipdl.py @@ -6,214 +6,291 @@ import os import sys from configparser import RawConfigParser from io import StringIO +from multiprocessing import Manager import ipdl from ipdl.ast import SYNC -def log(minv, fmt, *args): - if _verbosity >= minv: - print(fmt % args) - - -# process command line - - -op = optparse.OptionParser(usage="ipdl.py [options] IPDLfiles...") -op.add_option( - "-I", - "--include", - dest="includedirs", - default=[], - action="append", - help="Additional directory to search for included protocol specifications", -) -op.add_option( - "-s", - "--sync-msg-list", - dest="syncMsgList", - default="sync-messages.ini", - help="Config file listing allowed sync messages", -) -op.add_option( - "-m", - "--msg-metadata", - dest="msgMetadata", - default="message-metadata.ini", - help="Predicted message sizes for reducing serialization malloc overhead.", -) -op.add_option( - "-v", - "--verbose", - dest="verbosity", - default=1, - action="count", - help="Verbose logging (specify -vv or -vvv for very verbose logging)", -) -op.add_option( - "-q", - "--quiet", - dest="verbosity", - action="store_const", - const=0, - help="Suppress logging output", -) -op.add_option( - "-d", - "--outheaders-dir", - dest="headersdir", - default=".", - help="""Directory into which C++ headers will be generated. -A protocol Foo in the namespace bar will cause the headers - dir/bar/Foo.h, dir/bar/FooParent.h, and dir/bar/FooParent.h -to be generated""", -) -op.add_option( - "-o", - "--outcpp-dir", - dest="cppdir", - default=".", - help="""Directory into which C++ sources will be generated -A protocol Foo in the namespace bar will cause the sources - cppdir/FooParent.cpp, cppdir/FooChild.cpp -to be generated""", -) -op.add_option( - "-F", - "--file-list", - dest="file_list_file", - default=None, - help="""A file containing IPDL files to parse. This will be -merged with files provided on the commandline.""", -) - -options, cmdline_files = op.parse_args() -_verbosity = options.verbosity -syncMsgList = options.syncMsgList -msgMetadata = options.msgMetadata -headersdir = options.headersdir -cppdir = options.cppdir -includedirs = [os.path.abspath(incdir) for incdir in options.includedirs] - -files = [] - -if options.file_list_file is not None: - with open(options.file_list_file) as f: - files.extend(f.read().splitlines()) - -files.extend(cmdline_files) - -if not len(files): - op.error("No IPDL files specified") - -ipcmessagestartpath = os.path.join(headersdir, "IPCMessageStart.h") -ipc_msgtype_name_path = os.path.join(cppdir, "IPCMessageTypeName.cpp") - -log(2, 'Generated C++ headers will be generated relative to "%s"', headersdir) -log(2, 'Generated C++ sources will be generated in "%s"', cppdir) - -allmessages = {} -allsyncmessages = [] -allmessageprognames = [] -allprotocols = [] - - -def normalizedFilename(f): - if f == "-": - return "" - return f - - -log(2, "Reading sync message list") -parser = RawConfigParser() -parser.read_file(open(options.syncMsgList)) -syncMsgList = parser.sections() - -for section in syncMsgList: - if not parser.get(section, "description"): - print("Error: Sync message %s lacks a description" % section, file=sys.stderr) - sys.exit(1) - -# Read message metadata. Right now we only have 'segment_capacity' -# for the standard segment size used for serialization. -log(2, "Reading message metadata...") -msgMetadataConfig = RawConfigParser() -msgMetadataConfig.read_file(open(options.msgMetadata)) - -segmentCapacityDict = {} -for msgName in msgMetadataConfig.sections(): - if msgMetadataConfig.has_option(msgName, "segment_capacity"): - capacity = msgMetadataConfig.get(msgName, "segment_capacity") - segmentCapacityDict[msgName] = capacity - -# First pass: parse and type-check all protocols -for f in files: - log(2, os.path.basename(f)) - filename = normalizedFilename(f) - if f == "-": - fd = sys.stdin - else: - fd = open(f) - - specstring = fd.read() - fd.close() - - ast = ipdl.parse(specstring, filename, includedirs=includedirs) - if ast is None: - print("Specification could not be parsed.", file=sys.stderr) - sys.exit(1) +class WorkerPool: + per_process_context = None + + def __init__( + self, + manager, + files, + asts, + headersdir, + cppdir, + segmentCapacityDict, + allmessages, + allprotocols, + allmessageprognames, + allsyncmessages, + *, + processes=None + ): + if processes is None: + processes = os.cpu_count() or 1 + processes = min(processes, 8) + self.n = len(files) + self.asts = asts + self.pool = manager.Pool( + initializer=WorkerPool._init_worker, + initargs=( + files, + asts, + headersdir, + cppdir, + segmentCapacityDict, + allmessages, + allprotocols, + allmessageprognames, + allsyncmessages, + ), + processes=processes, + ) - log(2, "checking types") - if not ipdl.typecheck(ast): - print("Specification is not well typed.", file=sys.stderr) + def run(self): + self.pool.map(WorkerPool._run_worker, range(self.n)) + + @staticmethod + def _init_worker(*state): + WorkerPool.per_process_context = state + + @staticmethod + def _run_worker(index): + ( + files, + asts, + headersdir, + cppdir, + segmentCapacityDict, + allmessages, + allprotocols, + allmessageprognames, + allsyncmessages, + ) = WorkerPool.per_process_context + ast = asts[index] + ipdl.gencxx(files[index], ast, headersdir, cppdir, segmentCapacityDict) + + if ast.protocol: + allmessages[ast.protocol.name] = ipdl.genmsgenum(ast) + allprotocols.append(ast.protocol.name) + + # e.g. PContent::RequestMemoryReport (not prefixed or suffixed.) + for md in ast.protocol.messageDecls: + allmessageprognames.append("%s::%s" % (md.namespace, md.decl.progname)) + + if md.sendSemantics is SYNC: + allsyncmessages.append( + "%s__%s" % (ast.protocol.name, md.prettyMsgName()) + ) + + +def main(): + def log(minv, fmt, *args): + if _verbosity >= minv: + print(fmt % args) + + # process command line + + op = optparse.OptionParser(usage="ipdl.py [options] IPDLfiles...") + op.add_option( + "-I", + "--include", + dest="includedirs", + default=[], + action="append", + help="Additional directory to search for included protocol specifications", + ) + op.add_option( + "-s", + "--sync-msg-list", + dest="syncMsgList", + default="sync-messages.ini", + help="Config file listing allowed sync messages", + ) + op.add_option( + "-m", + "--msg-metadata", + dest="msgMetadata", + default="message-metadata.ini", + help="Predicted message sizes for reducing serialization malloc overhead.", + ) + op.add_option( + "-v", + "--verbose", + dest="verbosity", + default=1, + action="count", + help="Verbose logging (specify -vv or -vvv for very verbose logging)", + ) + op.add_option( + "-q", + "--quiet", + dest="verbosity", + action="store_const", + const=0, + help="Suppress logging output", + ) + op.add_option( + "-d", + "--outheaders-dir", + dest="headersdir", + default=".", + help="""Directory into which C++ headers will be generated. + A protocol Foo in the namespace bar will cause the headers + dir/bar/Foo.h, dir/bar/FooParent.h, and dir/bar/FooParent.h + to be generated""", + ) + op.add_option( + "-o", + "--outcpp-dir", + dest="cppdir", + default=".", + help="""Directory into which C++ sources will be generated + A protocol Foo in the namespace bar will cause the sources + cppdir/FooParent.cpp, cppdir/FooChild.cpp + to be generated""", + ) + op.add_option( + "-F", + "--file-list", + dest="file_list_file", + default=None, + help="""A file containing IPDL files to parse. This will be + merged with files provided on the commandline.""", + ) + + options, cmdline_files = op.parse_args() + _verbosity = options.verbosity + syncMsgList = options.syncMsgList + headersdir = options.headersdir + cppdir = options.cppdir + includedirs = [os.path.abspath(incdir) for incdir in options.includedirs] + + files = [] + + if options.file_list_file is not None: + with open(options.file_list_file) as f: + files.extend(f.read().splitlines()) + + files.extend(cmdline_files) + + if not len(files): + op.error("No IPDL files specified") + + ipcmessagestartpath = os.path.join(headersdir, "IPCMessageStart.h") + ipc_msgtype_name_path = os.path.join(cppdir, "IPCMessageTypeName.cpp") + + log(2, 'Generated C++ headers will be generated relative to "%s"', headersdir) + log(2, 'Generated C++ sources will be generated in "%s"', cppdir) + + def normalizedFilename(f): + if f == "-": + return "" + return f + + log(2, "Reading sync message list") + parser = RawConfigParser() + parser.read_file(open(options.syncMsgList)) + syncMsgList = parser.sections() + + for section in syncMsgList: + if not parser.get(section, "description"): + print( + "Error: Sync message %s lacks a description" % section, file=sys.stderr + ) + sys.exit(1) + + # Read message metadata. Right now we only have 'segment_capacity' + # for the standard segment size used for serialization. + log(2, "Reading message metadata...") + msgMetadataConfig = RawConfigParser() + msgMetadataConfig.read_file(open(options.msgMetadata)) + + manager = Manager() + segmentCapacityDict = manager.dict() + allmessages = manager.dict() + allsyncmessages = manager.list() + allmessageprognames = manager.list() + allprotocols = manager.list() + + for msgName in msgMetadataConfig.sections(): + if msgMetadataConfig.has_option(msgName, "segment_capacity"): + capacity = msgMetadataConfig.get(msgName, "segment_capacity") + segmentCapacityDict[msgName] = capacity + + # First pass: parse and type-check all protocols + asts = [] + for f in files: + log(2, os.path.basename(f)) + filename = normalizedFilename(f) + if f == "-": + fd = sys.stdin + else: + fd = open(f) + + specstring = fd.read() + fd.close() + + ast = ipdl.parse(specstring, filename, includedirs=includedirs) + if ast is None: + print("Specification could not be parsed.", file=sys.stderr) + sys.exit(1) + + log(2, "checking types") + if not ipdl.typecheck(ast): + print("Specification is not well typed.", file=sys.stderr) + sys.exit(1) + + if not ipdl.checkSyncMessage(ast, syncMsgList): + print( + "Error: New sync IPC messages must be reviewed by an IPC peer and recorded in %s" + % options.syncMsgList, + file=sys.stderr, + ) # NOQA: E501 + sys.exit(1) + + asts.append(ast) + + if not ipdl.checkFixedSyncMessages(parser): + # Errors have alraedy been printed to stderr, just exit sys.exit(1) - if not ipdl.checkSyncMessage(ast, syncMsgList): + # Second pass: generate code + pool = WorkerPool( + manager, + files, + asts, + headersdir, + cppdir, + segmentCapacityDict, + allmessages, + allprotocols, + allmessageprognames, + allsyncmessages, + ) + pool.run() + + allprotocols.sort() + allsyncmessages.sort() + + # Check if we have undefined message names in segmentCapacityDict. + # This is a fool-proof of the 'message-metadata.ini' file. + undefinedMessages = set(segmentCapacityDict.keys()) - set(allmessageprognames) + if len(undefinedMessages) > 0: print( - "Error: New sync IPC messages must be reviewed by an IPC peer and recorded in %s" - % options.syncMsgList, - file=sys.stderr, - ) # NOQA: E501 + "Error: Undefined message names in message-metadata.ini:", file=sys.stderr + ) + print(undefinedMessages, file=sys.stderr) sys.exit(1) -if not ipdl.checkFixedSyncMessages(parser): - # Errors have alraedy been printed to stderr, just exit - sys.exit(1) - -# Second pass: generate code -for f in files: - # Read from parser cache - filename = normalizedFilename(f) - ast = ipdl.parse(None, filename, includedirs=includedirs) - ipdl.gencxx(filename, ast, headersdir, cppdir, segmentCapacityDict) - - if ast.protocol: - allmessages[ast.protocol.name] = ipdl.genmsgenum(ast) - allprotocols.append(ast.protocol.name) - - # e.g. PContent::RequestMemoryReport (not prefixed or suffixed.) - for md in ast.protocol.messageDecls: - allmessageprognames.append("%s::%s" % (md.namespace, md.decl.progname)) - - if md.sendSemantics is SYNC: - allsyncmessages.append( - "%s__%s" % (ast.protocol.name, md.prettyMsgName()) - ) - -allprotocols.sort() + ipcmsgstart = StringIO() -# Check if we have undefined message names in segmentCapacityDict. -# This is a fool-proof of the 'message-metadata.ini' file. -undefinedMessages = set(segmentCapacityDict.keys()) - set(allmessageprognames) -if len(undefinedMessages) > 0: - print("Error: Undefined message names in message-metadata.ini:", file=sys.stderr) - print(undefinedMessages, file=sys.stderr) - sys.exit(1) - -ipcmsgstart = StringIO() - -print( - """ + print( + """ // CODE GENERATED by ipdl.py. Do not edit. #ifndef IPCMessageStart_h @@ -221,14 +298,14 @@ print( enum IPCMessageStart { """, - file=ipcmsgstart, -) + file=ipcmsgstart, + ) -for name in allprotocols: - print(" %sMsgStart," % name, file=ipcmsgstart) + for name in allprotocols: + print(" %sMsgStart," % name, file=ipcmsgstart) -print( - """ + print( + """ LastMsgIndex }; @@ -236,12 +313,12 @@ static_assert(LastMsgIndex <= 65536, "need to update IPC_MESSAGE_MACRO"); #endif // ifndef IPCMessageStart_h """, - file=ipcmsgstart, -) + file=ipcmsgstart, + ) -ipc_msgtype_name = StringIO() -print( - """ + ipc_msgtype_name = StringIO() + print( + """ // CODE GENERATED by ipdl.py. Do not edit. #include @@ -253,19 +330,19 @@ using std::uint32_t; namespace { enum IPCMessages { -""", - file=ipc_msgtype_name, -) - -for protocol in sorted(allmessages.keys()): - for msg, num in allmessages[protocol].idnums: - if num: - print(" %s = %s," % (msg, num), file=ipc_msgtype_name) - elif not msg.endswith("End"): - print(" %s__%s," % (protocol, msg), file=ipc_msgtype_name) - -print( - """ + """, + file=ipc_msgtype_name, + ) + + for protocol in sorted(allmessages.keys()): + for msg, num in allmessages[protocol].idnums: + if num: + print(" %s = %s," % (msg, num), file=ipc_msgtype_name) + elif not msg.endswith("End"): + print(" %s__%s," % (protocol, msg), file=ipc_msgtype_name) + + print( + """ }; } // anonymous namespace @@ -276,14 +353,14 @@ bool IPCMessageTypeIsSync(uint32_t aMessageType) { switch (aMessageType) { """, - file=ipc_msgtype_name, -) + file=ipc_msgtype_name, + ) -for msg in allsyncmessages: - print(" case %s:" % msg, file=ipc_msgtype_name) + for msg in allsyncmessages: + print(" case %s:" % msg, file=ipc_msgtype_name) -print( - """ return true; + print( + """ return true; default: return false; } @@ -292,24 +369,24 @@ print( const char* StringFromIPCMessageType(uint32_t aMessageType) { switch (aMessageType) { -""", - file=ipc_msgtype_name, -) - -for protocol in sorted(allmessages.keys()): - for msg, num in allmessages[protocol].idnums: - if num or msg.endswith("End"): - continue - print( - """ + """, + file=ipc_msgtype_name, + ) + + for protocol in sorted(allmessages.keys()): + for msg, num in allmessages[protocol].idnums: + if num or msg.endswith("End"): + continue + print( + """ case %s__%s: return "%s::%s";""" - % (protocol, msg, protocol, msg), - file=ipc_msgtype_name, - ) + % (protocol, msg, protocol, msg), + file=ipc_msgtype_name, + ) -print( - """ + print( + """ case DATA_PIPE_CLOSED_MESSAGE_TYPE: return "DATA_PIPE_CLOSED_MESSAGE"; case DATA_PIPE_BYTES_CONSUMED_MESSAGE_TYPE: @@ -353,15 +430,15 @@ namespace ipc { const char* ProtocolIdToName(IPCMessageStart aId) { switch (aId) { """, - file=ipc_msgtype_name, -) + file=ipc_msgtype_name, + ) -for name in allprotocols: - print(" case %sMsgStart:" % name, file=ipc_msgtype_name) - print(' return "%s";' % name, file=ipc_msgtype_name) + for name in allprotocols: + print(" case %sMsgStart:" % name, file=ipc_msgtype_name) + print(' return "%s";' % name, file=ipc_msgtype_name) -print( - """ + print( + """ default: return ""; } @@ -370,8 +447,12 @@ print( } // namespace ipc } // namespace mozilla """, - file=ipc_msgtype_name, -) + file=ipc_msgtype_name, + ) + + ipdl.writeifmodified(ipcmsgstart.getvalue(), ipcmessagestartpath) + ipdl.writeifmodified(ipc_msgtype_name.getvalue(), ipc_msgtype_name_path) + -ipdl.writeifmodified(ipcmsgstart.getvalue(), ipcmessagestartpath) -ipdl.writeifmodified(ipc_msgtype_name.getvalue(), ipc_msgtype_name_path) +if __name__ == "__main__": + main() diff --git a/ipc/ipdl/ipdl/__init__.py b/ipc/ipdl/ipdl/__init__.py index c042c16444..466a771c4e 100644 --- a/ipc/ipdl/ipdl/__init__.py +++ b/ipc/ipdl/ipdl/__init__.py @@ -85,7 +85,7 @@ def genmsgenum(ast): def writeifmodified(contents, file): contents = contents.encode("utf-8") dir = os.path.dirname(file) - os.path.exists(dir) or os.makedirs(dir) + os.makedirs(dir, exist_ok=True) oldcontents = None if os.path.exists(file): diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py index 291e1c698f..0e8b5e8dad 100644 --- a/ipc/ipdl/ipdl/lower.py +++ b/ipc/ipdl/ipdl/lower.py @@ -474,10 +474,6 @@ def errfnSentinel(rvalue=ExprLiteral.FALSE): return inner -def _destroyMethod(): - return ExprVar("ActorDestroy") - - def errfnUnreachable(msg): return [_logicError(msg)] @@ -3943,57 +3939,8 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): self.cls.addstmts([meth, refmeth, Whitespace.NL]) - # AllManagedActors(Array& inout) const - arrvar = ExprVar("arr__") - managedmeth = MethodDefn( - MethodDecl( - "AllManagedActors", - params=[ - Decl( - _cxxArrayType(_refptr(_cxxLifecycleProxyType()), ref=True), - arrvar.name, - ) - ], - methodspec=MethodSpec.OVERRIDE, - const=True, - ) - ) - - # Count the number of managed actors, and allocate space in the output array. - managedmeth.addcode( - """ - uint32_t total = 0; - """ - ) - for managed in ptype.manages: - managedmeth.addcode( - """ - total += ${container}.Count(); - """, - container=p.managedVar(managed, self.side), - ) - managedmeth.addcode( - """ - arr__.SetCapacity(total); - - """ - ) - - for managed in ptype.manages: - managedmeth.addcode( - """ - for (auto* key : ${container}) { - arr__.AppendElement(key->GetLifecycleProxy()); - } - - """, - container=p.managedVar(managed, self.side), - ) - - self.cls.addstmts([managedmeth, Whitespace.NL]) - # AllManagedActorsCount() const - managedmeth = MethodDefn( + managedcount = MethodDefn( MethodDecl( "AllManagedActorsCount", ret=Type.UINT32, @@ -4003,26 +3950,26 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): ) # Count the number of managed actors. - managedmeth.addcode( + managedcount.addcode( """ uint32_t total = 0; """ ) for managed in ptype.manages: - managedmeth.addcode( + managedcount.addcode( """ total += ${container}.Count(); """, container=p.managedVar(managed, self.side), ) - managedmeth.addcode( + managedcount.addcode( """ return total; """ ) - self.cls.addstmts([managedmeth, Whitespace.NL]) + self.cls.addstmts([managedcount, Whitespace.NL]) # OpenPEndpoint(...)/BindPEndpoint(...) for managed in ptype.manages: @@ -4188,66 +4135,50 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): ] ) - clearsubtreevar = ExprVar("ClearSubtree") + # DoomSubtree() + doomsubtree = MethodDefn( + MethodDecl("DoomSubtree", methodspec=MethodSpec.OVERRIDE) + ) - if ptype.isToplevel(): - # OnChannelClose() - onclose = MethodDefn( - MethodDecl("OnChannelClose", methodspec=MethodSpec.OVERRIDE) - ) - onclose.addcode( + for managed in ptype.manages: + doomsubtree.addcode( """ - DestroySubtree(NormalShutdown); - ClearSubtree(); - DeallocShmems(); - if (GetLifecycleProxy()) { - GetLifecycleProxy()->Release(); + for (auto* key : ${container}) { + key->DoomSubtree(); } - """ + """, + container=p.managedVar(managed, self.side), ) - self.cls.addstmts([onclose, Whitespace.NL]) - # OnChannelError() - onerror = MethodDefn( - MethodDecl("OnChannelError", methodspec=MethodSpec.OVERRIDE) - ) - onerror.addcode( - """ - DestroySubtree(AbnormalShutdown); - ClearSubtree(); - DeallocShmems(); - if (GetLifecycleProxy()) { - GetLifecycleProxy()->Release(); - } - """ - ) - self.cls.addstmts([onerror, Whitespace.NL]) + doomsubtree.addcode("SetDoomed();\n") - # private methods - self.cls.addstmt(Label.PRIVATE) + self.cls.addstmts([doomsubtree, Whitespace.NL]) + + # IProtocol* PeekManagedActor() override + peekmanagedactor = MethodDefn( + MethodDecl( + "PeekManagedActor", + methodspec=MethodSpec.OVERRIDE, + ret=Type("IProtocol", ptr=True), + ) + ) - # ClearSubtree() - clearsubtree = MethodDefn(MethodDecl(clearsubtreevar.name)) for managed in ptype.manages: - clearsubtree.addcode( + peekmanagedactor.addcode( """ - for (auto* key : ${container}) { - key->ClearSubtree(); - } - for (auto* key : ${container}) { - // Recursively releasing ${container} kids. - auto* proxy = key->GetLifecycleProxy(); - NS_IF_RELEASE(proxy); + if (IProtocol* actor = ${container}.Peek()) { + return actor; } - ${container}.Clear(); - """, container=p.managedVar(managed, self.side), ) - # don't release our own IPC reference: either the manager will do it, - # or we're toplevel - self.cls.addstmts([clearsubtree, Whitespace.NL]) + peekmanagedactor.addcode("return nullptr;\n") + + self.cls.addstmts([peekmanagedactor, Whitespace.NL]) + + # private methods + self.cls.addstmt(Label.PRIVATE) if not ptype.isToplevel(): self.cls.addstmts( @@ -4382,16 +4313,9 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): ) case = ExprCode( """ - { - ${manageecxxtype} actor = static_cast<${manageecxxtype}>(aListener); - - const bool removed = ${container}.EnsureRemoved(actor); - MOZ_RELEASE_ASSERT(removed, "actor not managed by this!"); + ${container}.EnsureRemoved(static_cast<${manageecxxtype}>(aListener)); + return; - auto* proxy = actor->GetLifecycleProxy(); - NS_IF_RELEASE(proxy); - return; - } """, manageecxxtype=manageecxxtype, container=p.managedVar(manageeipdltype, self.side), @@ -4708,14 +4632,18 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): return [ StmtCode( """ - if (!${actor}) { - NS_WARNING("Cannot bind null ${actorname} actor"); - return ${errfn}; - } + if (!${actor}) { + NS_WARNING("Cannot bind null ${actorname} actor"); + return ${errfn}; + } - ${actor}->SetManagerAndRegister($,{setManagerArgs}); - ${container}.Insert(${actor}); - """, + if (${actor}->SetManagerAndRegister($,{setManagerArgs})) { + ${container}.Insert(${actor}); + } else { + NS_WARNING("Failed to bind ${actorname} actor"); + return ${errfn}; + } + """, actor=actordecl.var(), actorname=actorproto.name() + self.side.capitalize(), errfn=errfn, @@ -4766,22 +4694,13 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): return method, (lbl, case) def destroyActor(self, md, actorexpr, why=_DestroyReason.Deletion): - if md and md.decl.type.isCtor(): - destroyedType = md.decl.type.constructedType() - else: - destroyedType = self.protocol.decl.type - return [ StmtCode( """ - IProtocol* mgr = ${actor}->Manager(); - ${actor}->DestroySubtree(${why}); - ${actor}->ClearSubtree(); - mgr->RemoveManagee(${protoId}, ${actor}); + ${actor}->ActorDisconnected(${why}); """, actor=actorexpr, why=why, - protoId=_protocolId(destroyedType), ) ] diff --git a/ipc/ipdl/test/gtest/PTestDestroyNested.ipdl b/ipc/ipdl/test/gtest/PTestDestroyNested.ipdl new file mode 100644 index 0000000000..1d58f1cf0c --- /dev/null +++ b/ipc/ipdl/test/gtest/PTestDestroyNested.ipdl @@ -0,0 +1,18 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PTestDestroyNestedSub; + +namespace mozilla { +namespace _ipdltest { + +[ChildProc=any, ChildImpl=virtual, ParentImpl=virtual] +async protocol PTestDestroyNested { + manages PTestDestroyNestedSub; +child: + async PTestDestroyNestedSub(); +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/gtest/PTestDestroyNestedSub.ipdl b/ipc/ipdl/test/gtest/PTestDestroyNestedSub.ipdl new file mode 100644 index 0000000000..033c49261d --- /dev/null +++ b/ipc/ipdl/test/gtest/PTestDestroyNestedSub.ipdl @@ -0,0 +1,19 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PTestDestroyNested; + +namespace mozilla { +namespace _ipdltest { + +[ChildProc=any, ChildImpl=virtual, ParentImpl=virtual] +async protocol PTestDestroyNestedSub { + manager PTestDestroyNested; +child: + + async __delete__(); +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/gtest/TestDestroyNested.cpp b/ipc/ipdl/test/gtest/TestDestroyNested.cpp new file mode 100644 index 0000000000..a348357b51 --- /dev/null +++ b/ipc/ipdl/test/gtest/TestDestroyNested.cpp @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Test an actor being destroyed during another ActorDestroy. + */ + +#include "gtest/gtest.h" + +#include "mozilla/_ipdltest/IPDLUnitTest.h" +#include "mozilla/_ipdltest/PTestDestroyNestedChild.h" +#include "mozilla/_ipdltest/PTestDestroyNestedParent.h" +#include "mozilla/_ipdltest/PTestDestroyNestedSubChild.h" +#include "mozilla/_ipdltest/PTestDestroyNestedSubParent.h" + +#include "mozilla/SpinEventLoopUntil.h" + +using namespace mozilla::ipc; + +namespace mozilla::_ipdltest { + +class TestDestroyNestedSubParent : public PTestDestroyNestedSubParent { + NS_INLINE_DECL_REFCOUNTING(TestDestroyNestedSubParent, override) + + void ActorDestroy(ActorDestroyReason aReason) override; + + bool mActorDestroyCalled = false; + bool mCloseManager = false; + + nsrefcnt GetRefCnt() const { return mRefCnt; } + + private: + ~TestDestroyNestedSubParent() = default; +}; + +class TestDestroyNestedSubChild : public PTestDestroyNestedSubChild { + NS_INLINE_DECL_REFCOUNTING(TestDestroyNestedSubChild, override) + private: + ~TestDestroyNestedSubChild() = default; +}; + +class TestDestroyNestedParent : public PTestDestroyNestedParent { + NS_INLINE_DECL_REFCOUNTING(TestDestroyNestedParent, override) + + void ActorDestroy(ActorDestroyReason aReason) override; + + bool mActorDestroyCalled = false; + + private: + ~TestDestroyNestedParent() = default; +}; + +class TestDestroyNestedChild : public PTestDestroyNestedChild { + NS_INLINE_DECL_REFCOUNTING(TestDestroyNestedChild, override) + + already_AddRefed AllocPTestDestroyNestedSubChild() + override { + return MakeAndAddRef(); + } + + private: + ~TestDestroyNestedChild() = default; +}; + +void TestDestroyNestedSubParent::ActorDestroy(ActorDestroyReason aReason) { + // Destroy our manager from within ActorDestroy() and assert that we don't + // re-enter. + EXPECT_FALSE(mActorDestroyCalled) << "re-entered ActorDestroy()"; + mActorDestroyCalled = true; + + if (mCloseManager) { + EXPECT_FALSE( + static_cast(Manager())->mActorDestroyCalled) + << "manager already destroyed"; + Manager()->Close(); + EXPECT_TRUE( + static_cast(Manager())->mActorDestroyCalled) + << "manager successfully destroyed"; + } + + // Make sure we also process any pending events, because we might be spinning + // a nested event loop within `ActorDestroy`. + NS_ProcessPendingEvents(nullptr); +} + +void TestDestroyNestedParent::ActorDestroy(ActorDestroyReason aReason) { + // Destroy our manager from within ActorDestroy() and assert that we don't + // re-enter. + EXPECT_FALSE(mActorDestroyCalled) << "re-entered ActorDestroy()"; + mActorDestroyCalled = true; +} + +IPDL_TEST(TestDestroyNested) { + auto p = MakeRefPtr(); + p->mCloseManager = true; + auto* rv1 = mActor->SendPTestDestroyNestedSubConstructor(p); + ASSERT_EQ(p, rv1) << "can't allocate Sub"; + + bool rv2 = PTestDestroyNestedSubParent::Send__delete__(p); + ASSERT_TRUE(rv2) + << "Send__delete__ failed"; + + ASSERT_TRUE(mActor->mActorDestroyCalled) + << "Parent not destroyed"; + ASSERT_TRUE(p->mActorDestroyCalled) + << "Sub not destroyed"; + + ASSERT_EQ(p->GetRefCnt(), 1u) << "Outstanding references to Sub remain"; + + // Try to allocate a new actor under the already-destroyed manager, and ensure + // that it is destroyed as expected. + auto p2 = MakeRefPtr(); + auto* rv3 = mActor->SendPTestDestroyNestedSubConstructor(p2); + ASSERT_EQ(rv3, nullptr) << "construction succeeded unexpectedly"; + + ASSERT_TRUE(p2->mActorDestroyCalled) + << "Sub not destroyed"; +} + +} // namespace mozilla::_ipdltest diff --git a/ipc/ipdl/test/gtest/moz.build b/ipc/ipdl/test/gtest/moz.build index 86fa2eca9e..5b60532026 100644 --- a/ipc/ipdl/test/gtest/moz.build +++ b/ipc/ipdl/test/gtest/moz.build @@ -22,6 +22,7 @@ SOURCES += [ "TestCrossProcessSemaphore.cpp", "TestDataStructures.cpp", "TestDescendant.cpp", + "TestDestroyNested.cpp", "TestEndpointOpens.cpp", "TestHangs.cpp", "TestInduceConnectionError.cpp", @@ -49,6 +50,8 @@ IPDL_SOURCES += [ "PTestDescendant.ipdl", "PTestDescendantSub.ipdl", "PTestDescendantSubsub.ipdl", + "PTestDestroyNested.ipdl", + "PTestDestroyNestedSub.ipdl", "PTestEndpointOpens.ipdl", "PTestEndpointOpensOpened.ipdl", "PTestHangs.ipdl", -- cgit v1.2.3