1
0
mirror of https://github.com/danog/MadelineProto.git synced 2024-12-04 18:07:49 +01:00
MadelineProto/src/AsyncTools.php

175 lines
5.5 KiB
PHP
Raw Normal View History

2022-12-30 21:54:44 +01:00
<?php
declare(strict_types=1);
2022-12-30 19:25:28 +01:00
/**
* Tools module.
*
* This file is part of MadelineProto.
* MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
* You should have received a copy of the GNU General Public License along with MadelineProto.
* If not, see <http://www.gnu.org/licenses/>.
*
* @author Daniil Gentili <daniil@daniil.it>
2023-01-04 12:43:01 +01:00
* @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
2022-12-30 19:25:28 +01:00
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
* @link https://docs.madelineproto.xyz MadelineProto documentation
*/
namespace danog\MadelineProto;
2023-01-11 18:47:27 +01:00
use Amp\Cancellation;
use Amp\CancelledException;
use Amp\DeferredCancellation;
2022-12-30 20:24:13 +01:00
use Amp\Future;
2022-12-30 19:25:28 +01:00
use Amp\TimeoutException;
2023-01-15 20:13:47 +01:00
use Closure;
2022-12-30 19:25:28 +01:00
use Generator;
use Revolt\EventLoop;
2022-12-30 21:54:44 +01:00
use Throwable;
2022-12-30 19:25:28 +01:00
use const LOCK_NB;
use const LOCK_UN;
2022-12-30 20:24:13 +01:00
use function Amp\async;
2022-12-30 19:25:28 +01:00
use function Amp\ByteStream\getOutputBufferStream;
use function Amp\ByteStream\getStdin;
use function Amp\ByteStream\getStdout;
use function Amp\delay;
/**
* Async tools.
*/
abstract class AsyncTools extends StrTools
{
/**
* Rethrow exception into event loop.
*/
2023-07-04 18:19:06 +02:00
public static function rethrow(Throwable $e): void
{
EventLoop::queue(fn () => throw $e);
}
2022-12-30 19:25:28 +01:00
/**
2023-06-25 16:59:56 +02:00
* Fork a new green thread and execute the passed function in the background.
*
* @template T
*
2023-06-25 17:02:20 +02:00
* @param \Closure(...):T $callable Function to execute
2023-06-25 16:59:56 +02:00
* @param mixed ...$args Arguments forwarded to the function when forking the thread.
*
* @return Future<T>
2022-12-30 19:25:28 +01:00
*
* @psalm-suppress InvalidScope
*/
2023-06-25 16:59:56 +02:00
public static function callFork(callable|Generator|Future $callable, ...$args): Future
2022-12-30 19:25:28 +01:00
{
2023-06-25 16:59:56 +02:00
if (\is_callable($callable)) {
$callable = async($callable, ...$args);
2022-12-30 19:25:28 +01:00
}
2023-06-25 16:59:56 +02:00
return $callable;
2022-12-30 19:25:28 +01:00
}
/**
* Asynchronously lock a file
* Resolves with a callbable that MUST eventually be called in order to release the lock.
*
* @param string $file File to lock
* @param integer $operation Locking mode
* @param float $polling Polling interval
2023-01-14 19:51:23 +01:00
* @param ?Cancellation $token Cancellation token
2023-01-15 20:13:47 +01:00
* @param ?Closure $failureCb Failure callback, called only once if the first locking attempt fails.
* @return ($token is null ? (Closure(): void) : ((Closure(): void)|null))
2022-12-30 19:25:28 +01:00
*/
2023-01-15 20:13:47 +01:00
public static function flock(string $file, int $operation, float $polling = 0.1, ?Cancellation $token = null, ?Closure $failureCb = null): ?Closure
2022-12-30 19:25:28 +01:00
{
2023-05-25 15:32:57 +02:00
if (!\file_exists($file)) {
2023-04-23 23:28:31 +02:00
\touch($file);
2022-12-30 19:25:28 +01:00
}
$operation |= LOCK_NB;
$res = \fopen($file, 'c');
do {
$result = \flock($res, $operation);
if (!$result) {
if ($failureCb) {
EventLoop::queue($failureCb);
2022-12-30 19:25:28 +01:00
$failureCb = null;
}
if ($token) {
2023-01-15 20:13:47 +01:00
if ($token->isRequested()) {
return null;
}
2023-01-11 18:47:27 +01:00
try {
delay($polling, true, $token);
} catch (CancelledException) {
2022-12-30 21:43:58 +01:00
return null;
2022-12-30 19:25:28 +01:00
}
} else {
delay($polling);
}
}
} while (!$result);
return static function () use (&$res): void {
if ($res) {
\flock($res, LOCK_UN);
\fclose($res);
$res = null;
}
};
}
/**
* Asynchronously sleep.
*
* @param float $time Number of seconds to sleep for
*/
public static function sleep(float $time): void
{
delay($time);
}
/**
* @internal
*/
public static function getTimeoutCancellation(float $timeout, string $message = "Operation timed out"): Cancellation
{
2023-07-27 13:20:30 +02:00
$e = new TimeoutException($message);
$deferred = new DeferredCancellation;
2023-07-27 13:20:30 +02:00
EventLoop::delay($timeout, fn () => $deferred->cancel($e));
return $deferred->getCancellation();
}
2022-12-30 19:25:28 +01:00
/**
* Asynchronously read line.
*
* @param string $prompt Prompt
*/
public static function readLine(string $prompt = '', ?Cancellation $cancel = null): string
2022-12-30 19:25:28 +01:00
{
try {
Magic::togglePeriodicLogging();
$stdin = getStdin();
$stdout = getStdout();
if ($prompt) {
$stdout->write($prompt);
}
static $lines = [''];
while (\count($lines) < 2 && ($chunk = $stdin->read($cancel)) !== null) {
2022-12-30 19:25:28 +01:00
$chunk = \explode("\n", \str_replace(["\r", "\n\n"], "\n", $chunk));
$lines[\count($lines) - 1] .= \array_shift($chunk);
$lines = \array_merge($lines, $chunk);
}
} finally {
Magic::togglePeriodicLogging();
}
2023-03-17 11:30:40 +01:00
return \array_shift($lines) ?? '';
2022-12-30 19:25:28 +01:00
}
/**
* Asynchronously write to stdout/browser.
*
* @param string $string Message to echo
*/
2022-12-30 20:24:13 +01:00
public static function echo(string $string): void
2022-12-30 19:25:28 +01:00
{
2022-12-30 20:24:13 +01:00
getOutputBufferStream()->write($string);
2022-12-30 19:25:28 +01:00
}
}