Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:109030 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 52100 invoked from network); 15 Mar 2020 18:42:24 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 15 Mar 2020 18:42:24 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id B5B3D1804E0 for ; Sun, 15 Mar 2020 10:04:36 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS2639 136.143.188.0/24 X-Spam-Virus: No X-Envelope-From: Received: from sender4-op-o11.zoho.com (sender4-op-o11.zoho.com [136.143.188.11]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Sun, 15 Mar 2020 10:04:34 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1584291870; cv=none; d=zohomail.com; s=zohoarc; b=MImTs/LwehHhLaLHf+sthcBUq1QMmfUbrOQx+zBKcg6CKUm41EK0EX8KbddP2NbvS8/hAmEoaDr89iuwxTyIYNxCZq3vNIY/RmmZm81bn/5ujFWpDTV5Po08oV6ea8QTmOLJcjWFtP5YNtAy090EEGsXorufBpW/gx1Ni7jJzuY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1584291870; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:To; bh=bB72VkZNlfCHTDAmbmeC14GT8+wF4TefVxy5UiKHgDE=; b=i05qjcTyH51xZ6NbJC3eJL22rsZG5MbUsy+BhJUt8VuIcr2UhQwMRXjGhXC4ha8roZ1zTxxV/rGwHa6+BoeWB69f+hnSOrcFzWzpFe52kgg7zY+nGfXImeFclL1bYeZONYbA+mGN6j1tRtfSHY9jSTBeYxF/TPBLhVIgR95BY4Y= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=manuelcanga.dev; spf=pass smtp.mailfrom=php@manuelcanga.dev; dmarc=pass header.from= header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1584291870; s=zoho; d=manuelcanga.dev; i=php@manuelcanga.dev; h=Date:From:To:Cc:Message-ID:In-Reply-To:References:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding; bh=bB72VkZNlfCHTDAmbmeC14GT8+wF4TefVxy5UiKHgDE=; b=OqM7ssSqn+lRpDg/x6b6ZANzth/HxqTobi9X5kLs/pDIAgrYzFqKUhWjTqhK+v9R gHbwTgz4kbEmk9h2Dpz1tRSNfdQpcID3HDgnqhlu1JtTwn6mzIGye8if/Fn5M+T7IE9 fb16KAzR+tBRWDPzBXwmWrT0GP5hxjThPBONiSKY= Received: from mail.zoho.com by mx.zohomail.com with SMTP id 1584291839180129.32305747798796; Sun, 15 Mar 2020 10:03:59 -0700 (PDT) Date: Sun, 15 Mar 2020 18:03:59 +0100 To: "Nikita Popov" Cc: "Marcio Almada" , "PHP internals" Message-ID: <170df263484.1211e7515181041.5210721846278089179@manuelcanga.dev> In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Importance: Medium User-Agent: Zoho Mail X-Mailer: Zoho Mail Subject: Re: [PHP-DEV] exit() via exception From: php@manuelcanga.dev (Manuel Canga) ---- En mi=C3=A9, 11 mar 2020 11:09:13 +0100 Nikita Popov escribi=C3=B3 ---- > On Fri, Oct 11, 2019 at 4:11 PM Nikita Popov wrot= e: >=20 > > On Fri, Oct 11, 2019 at 3:47 PM Marcio Almada > > wrote: > > > >> Em sex, 11 de out de 2019 =C3=A0s 08:05, Nikita Popov > >> escreveu: > >> > > >> > Hi, > >> > > >> > >> Hello :) > >> > >> > Currently exit() is implemented using bailout and unclean shutdown, > >> which > >> > means that we're going to perform a longjmp back to the top-level s= cope > >> and > >> > let the memory manager clean up all the memory it knows about. Anyt= hing > >> not > >> > allocated using ZMM is going to leak persistently. > >> > > >> > For me, one of the most annoying things about this is that we can't > >> perform > >> > proper leak checks on code using PhpUnit, because it will always ex= it() > >> at > >> > the end, which will result in "expected" memory leaks. > >> > > >> > I think it would be good to switch exit() to work by throwing a mag= ic > >> > exception, similar to what Python does. This would allow us to prop= erly > >> > unwind the stack, executing finally blocks (which are currently ski= pped) > >> > and perform a clean engine shutdown. > >> > > >> > Depending on the implementation, we could also allow code to actual= ly > >> catch > >> > this exception, which may be useful for testing scenarios, as well = as > >> > long-running daemons. > >> > > >> > I'm mainly wondering how exactly we'd go about integrating this in = the > >> > existing exception hierarchy. > >> > >> > Assuming that it is desirable to allow people > >> > to actually catch this exception > >> > my first thought would be along these > >> > lines: > >> > > >> > Throwable (convert to abstract class) > >> > \-> Exception > >> > \-> Error > >> > \-> ExitThrowable > >> > > >> > This does mean though that existing code using catch(Throwable) is > >> going to > >> > catch exit()s as well. This can be avoided by introducing *yet anot= her* > >> > super-class/interface above Throwable, which is something I'd rathe= r > >> avoid. > >> > > >> > >> Since you brought python as inspiration, I believe the hierarchy goes > >> like this on their land: > >> > >> BaseException > >> +-- SystemExit > >> +-- KeyboardInterrupt > >> +-- GeneratorExit > >> +-- Exception > >> +-- [kitchen sink] > >> > >> Being `BaseException` the base class for all built-in exceptions. It > >> is not meant to be directly > >> inherited by user-defined classes. It 's the equivalent to our > >> `Throwable` situation. In this context > >> `ExitThrowable -> Throwable ` appears legit. > >> > >> > > >> > Anyone have thoughts on this matter? > >> > > >> > >> Yes. There is an obvious can of worms if I've got this right: `exit()= ` > >> and `die()` would no longer guarantee a > >> program to actually terminate in case catching `ExitThrowable` is > >> allowed. Python solves this by actually > >> having two patterns: > >> > >> 1. `quit()`, `exit()`, `sys.exit()` are the equivalent to `raise > >> SystemExit`, can be caught / interrupted > >> 2. `os._exit()`, can't be caught but has a callback mechanism like ou= r > >> `register_shutdown_function`, > >> see https://docs.python.org/3/library/atexit.html > > > > > > I don't believe atexit applies to os._exit(). In any case, I agree tha= t > > this is something we're currently missing -- we should probably add a > > pcntl_exit() for this purpose. It should be noted though that this is > > really very different from exit(), which is still quite graceful and u= sable > > in a webserver context, while a hypothetical pcntl_exit() would bring = down > > the server process. As the Python docs mention, the primary use-case w= ould > > be exiting from forked processes without going through shutdown, which= has > > also recently come up in https://github.com/php/php-src/pull/4712. > > > > > >> If we bind `exit()` and `die()` to a catchable exception how would we > >> still have the scenario 2 available > >> on PHP land without a BCB? :) > >> > > > >> I have one simple suggestion: Introduce `EngineShutdown -> Throwable`= , > >> bind `exit|die` to it but disallow > >> `catch(\EngineShutdown $e)` at compile time. This would allow keeping > >> backwards compatibility to > >> scenario 2 without messing with our current exception hierarchy. > >> > > > > I think the options are basically: > > > > 1. Making EngineShutdown implement Throwable, which would make existin= g > > catch(Throwable) catch it -- probably a no-go. > > > > 2. Making EngineShutdown not implement Throwable, which means that not= all > > "exceptions" implement the interface, which is rather odd. It still al= lows > > explicitly catching the exit. > > > > 3. Introducing a function like catch_exit(function() { ... }). This wo= uld > > still allow catching exits (for phpunit + daemon use cases), but the f= act > > that this is actually implemented based on an exception would be hidde= n and > > the only way to catch the exit is through this function. > > > > 4. Don't allow catching exits at all. In this case the exception is ju= st > > an implementation detail. > > >=20 > I've started implementing variant 4 in > https://github.com/php/php-src/pull/5243. It uses an internal exception > type to implement exit() that cannot be caught. >=20 > Finally blocks do get executed, as they should be. This does mean that i= t > is possibly to discard the exit through finally, because control flow > performed in finally always wins: >=20 > try { > exit; > } finally { > (null)->method(); // throw Error > } >=20 > This snippet will throw an Error exception, with the original exit being > discarded. >=20 > try { > try { > exit; > } finally { > (null)->method(); // throw Error > } > } catch (Error $e) { > // Ignore > } >=20 > This snippet will catch the Error exception and thus stops unwinding. >=20 > I believe this behavior is correct and expected (by which I mean: > consistent with finally semantics), but it does mean that exits can be > discarded even without having an explicit "catch_exit" function or simil= ar. >=20 > Nikita >=20 Hello, About this subject: Nowadays, `register_shutdown_function`* function exists in order to custom = your the end of program. However, with this code: --- CODE ---