Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:126611 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by qa.php.net (Postfix) with ESMTPS id 78D841A00BC for ; Thu, 6 Mar 2025 22:26:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1741299817; bh=4U3n/voQjCi5lVjMojVHouJnUIwk0usEG/dA+IIccoE=; h=Date:Subject:To:References:From:In-Reply-To:From; b=UPvHvRHGrRW2s17nzCjQhyL24ItN3YuM4FcQGNl6cFXAi1oOeTB7nrdNtHyHjNt2G ZxKPnU9ErbudunxTRagmnOk2fWI/ypr5m9OZn2tbzIKxHVSDc7PwTDgRu57VKwqQJi q869Oypurdw5zGK+citJrGllHixGeCsFWs1ovw4s6YKlmokGkPyCdOp8QLV7m4Ji1U Mmt5O4ZnySGp1l5R0rLHPXfjQMOYHTFFj+gc1zbNA5uGzE81cMDI1ogA+Yv4ppaYCt VWf9DBSCz2MZQKkMOMorAVHPDgQIhjGXRi+wJWrbvWP6Ulus5TQP7y9Eun8nXAhtsA 6EKaz1QueWz3Q== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id EADE718004A for ; Thu, 6 Mar 2025 22:23:35 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-2.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,RCVD_IN_DNSWL_LOW, SPF_HELO_PASS,SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from fout-a5-smtp.messagingengine.com (fout-a5-smtp.messagingengine.com [103.168.172.148]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Thu, 6 Mar 2025 22:23:35 +0000 (UTC) Received: from phl-compute-05.internal (phl-compute-05.phl.internal [10.202.2.45]) by mailfout.phl.internal (Postfix) with ESMTP id 6B17513826F4 for ; Thu, 6 Mar 2025 17:26:10 -0500 (EST) Received: from phl-mailfrontend-01 ([10.202.2.162]) by phl-compute-05.internal (MEProxy); Thu, 06 Mar 2025 17:26:10 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=rwec.co.uk; h=cc :content-transfer-encoding:content-type:content-type:date:date :from:from:in-reply-to:in-reply-to:message-id:mime-version :references:reply-to:subject:subject:to:to; s=fm1; t=1741299970; x=1741386370; bh=uG0a0La0BtLtP9WECijaBgcXBDndriPQFMMBWRjAMwo=; b= hpjO3DcpvSoxXpDiWc9htESczvwKYeT0HsODBGnoLAu61kVn+MLAgY4k6d4PeBOS ZFrnQMmod2B41f0ASGnHPXQNmSLaHD1SHhRFri67uhdXG/gyxzeoDugnXuBEGbla ezlGIsbohfnbR6IW099hsH3Cqm+NnFWM/0qPkEOy7J36RH6c0SDjFfsnnlICA9f1 l1ok7ocYX+pKvM5pTF8sgkeNl8rjcD5D8ADrrrcDIov0oGDHlxCiKANqtPv9Tb4d nGM8S1EXk/Z51CVkR7gKdox/ZB8+p2xFHXz5SRAx0Y4kaMQFS2eNgrWA37o1tddZ FQDN8BHjm+BM+uKsbU9Lzw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-transfer-encoding:content-type :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm1; t=1741299970; x=1741386370; bh=u G0a0La0BtLtP9WECijaBgcXBDndriPQFMMBWRjAMwo=; b=jUunNM7kA5XYFVfey J35vHgZu2PSjX2VRMpQq95Xclnc6D+rD+MYHEDqylq2Gst9NQcc/pI2k4uq8D3q8 WzTW/TVYHmi7zNDQVCB7Yrw+Kud3YaHFDPXOs8LS0guae8GjJydA760QVTChONn1 G+M1gxfg/fLbWhBZ7ECj5pjXE0xtJDUU8+AKEcMTzWmaVfsaLeTxfAx0D25sw5sP RaphsrDjhmzfmqxSCuY8PyYk0ETuA43l2KZTu6s3+UuohTihoS/eyZLJzXBpa/jh JLYee/4Sqi2QsQ/Cl5N/VKkW9YpECaevt8HME5RDPFCe8GbTto5l8qk1w42/xCnH h6hgg== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddutdekleehucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggv pdfurfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucenucfjughrpefkff ggfgfuvfhfhfgjtgfgsehtkeertddtvdejnecuhfhrohhmpedftfhofigrnhcuvfhomhhm ihhnshculgfkoffuohfrngdfuceoihhmshhophdrphhhphesrhifvggtrdgtohdruhhkqe enucggtffrrghtthgvrhhnpeefhfettdeuveejkeevueduudfgjeeiteehheehvdeuudev hefhieffueffheejjeenucffohhmrghinhephhhtthhprdhonhgvnecuvehluhhsthgvrh fuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepihhmshhophdrphhhphesrhif vggtrdgtohdruhhkpdhnsggprhgtphhtthhopedupdhmohguvgepshhmthhpohhuthdprh gtphhtthhopehinhhtvghrnhgrlhhssehlihhsthhsrdhphhhprdhnvght X-ME-Proxy: Feedback-ID: id5114917:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA for ; Thu, 6 Mar 2025 17:26:09 -0500 (EST) Message-ID: <779046a0-7c70-45a9-82c4-7c31c502cec2@rwec.co.uk> Date: Thu, 6 Mar 2025 22:26:06 +0000 Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PHP-DEV] PHP True Async RFC To: internals@lists.php.net References: <9964db8c-0ffe-43d5-8246-47fc76b07180@app.fastmail.com> <78a03dd0-fd4a-4f4a-ad8a-37e5704f06fc@app.fastmail.com> <23e162f6-54b0-4564-9d79-7b3bdc3d1ab5@rwec.co.uk> <36cee7e3-2ef8-4f96-a72e-e67a99e5f9bb@rwec.co.uk> Content-Language: en-GB In-Reply-To: Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit From: imsop.php@rwec.co.uk ("Rowan Tommins [IMSoP]") On 06/03/2025 11:31, Edmond Dantes wrote: > For example, PHP has functions for working with HTTP. One of them > writes the last received headers into a "global" variable, and another > function allows retrieving them. This is where a context is needed. OK, let's dig into this case: what is the actual problem, and what does an async design need to provide so that it can be solved. As far as I know, all current SAPIs follow one of two patterns: 1) The traditional "shared nothing" approach: each request is launched in a new process or thread, and all global state is isolated to that request. 2) The explicit injection approach: the request and response are represented as objects, and the user must pass those objects around to where they are needed. Notably, 2 can be emulated on top of 1, but not vice versa, and this is exactly what a lot of modern applications and frameworks do: they take the SAPI's global state, and wrap it in injected objects (e.g. PSR-7 ServerRequestInterface and ServerResponseInterface). Code written that way will work fine on a SAPI that spawns a fiber for each request, so there's no problem for us to solve there. At the other extreme are frameworks and applications that access the global state directly throughout - making heavy use of superglobal, global, and static variables; directly outputting using echo/print, etc. Those will break in a fiber-based SAPI, but as far as I can see, there's nothing the async design can do to fix that. In the middle, there are some applications we *might* be able to help: they rely on global state, but wrap it in global functions or static methods which could be replaced with some magic from the async implementation. So our problem statement is: - given a function that takes no request-specific input, and is expected to return request-specific state (e.g. function get_query_string_param(string $name): ?string) - and, given a SAPI that spawns a fiber for each request - how do we adjust the implementation of the function, without changing its signature? Things we don't need to define: - how the SAPI works - how the data is structured inside the function Non-solutions: - refactoring the application to pass around a Context object - if we're willing to do that, we can just pass around a PSR-7 RequestInterface instead, and the problem goes away Minimal solution: - a way to get an integer or string, which the function can use to partition its data Usage example: function get_query_string_param(string $name): ?string {     global $request_data; // in a shared-nothing SAPI, this is per-request; but in a fiber-based one, it's shared between requests     $request_data_partition = $request_data[ Fiber::getCurrent()->getId() ]; // this line makes the function work under concurrent SAPIs     return $request_data_partition['query_string'][$name]; // this line is basically unchanged from the original application } Limitation: - if the SAPI spawns a fiber for the request, but that fiber then spawns child fibers, the function won't find the right partition Minimal solution: - track and expose the "parent" of each fiber Usage example: function get_query_string_param(string $name): ?string {     global $request_data;     // Traverse until we find the ID we've stored data against in our request bootstrapping code     $fiber = Fiber::getCurrent();     while ( ! isset($request_data[ $fiber->getId() ] ) {         $fiber = $fiber->getParent();     }     $request_data_partition = $request_data[ $fiber->getId() ];     return $request_data_partition['query_string'][$name]; } Obviously, this isn't the only solution, but it is sufficient for this problem. As a first pass, it saves us bikeshedding exactly what methods an Async\Context class should have, because that whole class can be added later, or just implemented in userland. If we strip down the solution initially, we can concentrate on the fundamental design - things like "Fibers have parents", and what that implies for how they're started and used. -- Rowan Tommins [IMSoP]