Skip to content

Commit ff61775

Browse files
authored
Merge pull request #3 from ace411/develop
v0.1.0 changes
2 parents 9f7e3e9 + 6d683da commit ff61775

File tree

8 files changed

+275
-8
lines changed

8 files changed

+275
-8
lines changed

README.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,12 @@ class Mrloop
5555
callable $callback,
5656
): void
5757
public tcpServer(int $port, callable $callback): void
58+
public writev(int $fd, string $message): void
5859
public static parseHttpRequest(string $request, int $headerlimit = 100): iterable
5960
public static parseHttpResponse(string $response, int $headerlimit = 100): iterable
6061
public addTimer(float $interval, callable $callback): void
6162
public addPeriodicTimer(float $interval, callable $callback): void
63+
public futureTick(callable $callback): void
6264
public addSignal(int $signal, callable $callback): void
6365
public run(): void
6466
public stop(): void
@@ -69,10 +71,12 @@ class Mrloop
6971
- [`Mrloop::addReadStream`](#mrloopaddreadstream)
7072
- [`Mrloop::addWriteStream`](#mrloopaddwritestream)
7173
- [`Mrloop::tcpServer`](#mrlooptcpserver)
74+
- [`Mrloop::writev`](#mrloopwritev)
7275
- [`Mrloop::parseHttpRequest`](#mrloopparsehttprequest)
7376
- [`Mrloop::parseHttpResponse`](#mrloopparsehttpresponse)
7477
- [`Mrloop::addTimer`](#mrloopaddtimer)
7578
- [`Mrloop::addPeriodicTimer`](#mrloopaddperiodictimer)
79+
- [`Mrloop::futureTick`](#mrloopfuturetick)
7680
- [`Mrloop::addSignal`](#mrloopaddsignal)
7781
- [`Mrloop::run`](#mrlooprun)
7882
- [`Mrloop::stop`](#mrloopstop)
@@ -242,6 +246,7 @@ Instantiates a simple TCP server.
242246
- **client** (iterable) - An array containing client socket information.
243247
- **client_addr** (string) - The client IP address.
244248
- **client_port** (integer) - The client socket port.
249+
- **client_fd** (integer) - The client socket file descriptor.
245250

246251
**Return value(s)**
247252

@@ -282,6 +287,60 @@ The example above will produce output similar to that in the snippet to follow.
282287
283288
```
284289

290+
### `Mrloop::writev`
291+
292+
```php
293+
public Mrloop::writev(int $fd, string $contents): void
294+
```
295+
296+
Performs vectorized non-blocking write operation on a specified file descriptor.
297+
298+
**Parameter(s)**
299+
300+
- **fd** (integer) - The file descriptor to write to.
301+
- **contents** (string) - The arbitrary contents to write.
302+
303+
**Return value(s)**
304+
305+
The parser will throw an exception in the event that an invalid file descriptor is encountered and will not return anything otherwise.
306+
307+
```php
308+
use ringphp\Mrloop;
309+
310+
$loop = Mrloop::init();
311+
312+
$loop->tcpServer(
313+
8080,
314+
function (string $message, iterable $client) use ($loop) {
315+
[
316+
'client_addr' => $addr,
317+
'client_port' => $port,
318+
'client_fd' => $fd,
319+
] = $client;
320+
321+
$loop->writev(
322+
$fd,
323+
\sprintf(
324+
"Hello, %s:%d\r\n",
325+
$addr,
326+
$port,
327+
),
328+
);
329+
},
330+
);
331+
332+
echo "Listening on port 8080\n";
333+
334+
$loop->run();
335+
```
336+
337+
The example above will produce output similar to that in the snippet to follow.
338+
339+
```
340+
Listening on port 8080
341+
342+
```
343+
285344
### `Mrloop::parseHttpRequest`
286345

287346
```php
@@ -584,6 +643,50 @@ Tick: 4
584643
Tick: 5
585644
```
586645

646+
### `Mrloop::futureTick`
647+
648+
```php
649+
public Mrloop::futureTick(callable $callback): void
650+
```
651+
652+
Schedules the execution of a specified action for the next event loop tick.
653+
654+
**Parameter(s)**
655+
656+
- **callback** (callable) - The function in which the action to be scheduled is defined.
657+
658+
**Return value(s)**
659+
660+
The function does not return anything.
661+
662+
```php
663+
use ringphp\Mrloop;
664+
665+
$loop = Mrloop::init();
666+
$tick = 0;
667+
668+
$loop->futureTick(
669+
function () use (&$tick) {
670+
echo \sprintf("Tick: %d\n", ++$tick);
671+
},
672+
);
673+
674+
$loop->futureTick(
675+
function () use (&$tick) {
676+
echo \sprintf("Tick: %d\n", ++$tick);
677+
},
678+
);
679+
680+
$loop->run();
681+
```
682+
683+
The example above will produce output similar to that in the snippet to follow.
684+
685+
```
686+
Tick: 1
687+
Tick: 2
688+
```
689+
587690
### `Mrloop::addSignal`
588691

589692
```php

mrloop_arginfo.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ ZEND_ARG_TYPE_INFO(0, contents, IS_STRING, 0)
5050
ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)
5151
ZEND_END_ARG_INFO()
5252

53+
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Mrloop_writev, 0, 0, 2)
54+
ZEND_ARG_TYPE_INFO(0, fd, IS_LONG, 0)
55+
ZEND_ARG_TYPE_INFO(0, contents, IS_STRING, 0)
56+
ZEND_END_ARG_INFO()
57+
58+
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Mrloop_futureTick, 0, 0, 1)
59+
ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)
60+
ZEND_END_ARG_INFO()
61+
5362
ZEND_METHOD(Mrloop, init);
5463
ZEND_METHOD(Mrloop, stop);
5564
ZEND_METHOD(Mrloop, run);
@@ -61,6 +70,8 @@ ZEND_METHOD(Mrloop, addSignal);
6170
ZEND_METHOD(Mrloop, addReadStream);
6271
ZEND_METHOD(Mrloop, addWriteStream);
6372
ZEND_METHOD(Mrloop, parseHttpResponse);
73+
ZEND_METHOD(Mrloop, writev);
74+
ZEND_METHOD(Mrloop, futureTick);
6475

6576
static const zend_function_entry class_Mrloop_methods[] = {
6677
PHP_ME(Mrloop, init, arginfo_class_Mrloop_init, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
@@ -74,4 +85,6 @@ static const zend_function_entry class_Mrloop_methods[] = {
7485
PHP_ME(Mrloop, addReadStream, arginfo_class_Mrloop_addReadStream, ZEND_ACC_PUBLIC)
7586
PHP_ME(Mrloop, addWriteStream, arginfo_class_Mrloop_addWriteStream, ZEND_ACC_PUBLIC)
7687
PHP_ME(Mrloop, parseHttpResponse, arginfo_class_Mrloop_parseHttpResponse, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
77-
PHP_FE_END};
88+
PHP_ME(Mrloop, writev, arginfo_class_Mrloop_writev, ZEND_ACC_PUBLIC)
89+
PHP_ME(Mrloop, futureTick, arginfo_class_Mrloop_futureTick, ZEND_ACC_PUBLIC)
90+
PHP_FE_END};

php_mrloop.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,20 @@ PHP_METHOD(Mrloop, addWriteStream)
8484
}
8585
/* }}} */
8686

87+
/* {{{ proto void Mrloop::writev( int fd [, string contents ] ) */
88+
PHP_METHOD(Mrloop, writev)
89+
{
90+
php_mrloop_writev(INTERNAL_FUNCTION_PARAM_PASSTHRU);
91+
}
92+
/* }}} */
93+
94+
/* {{{ proto void Mrloop::futureTick( callable callback ) */
95+
PHP_METHOD(Mrloop, futureTick)
96+
{
97+
php_mrloop_add_future_tick(INTERNAL_FUNCTION_PARAM_PASSTHRU);
98+
}
99+
/* }}} */
100+
87101
/* {{{ PHP_MINIT_FUNCTION */
88102
PHP_MINIT_FUNCTION(mrloop)
89103
{

src/loop.c

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ static int php_mrloop_timer_cb(void *data)
102102
zval_ptr_dtor(&result);
103103
efree(cb);
104104

105-
return type == PHP_MRLOOP_TIMER ? 0 : 1;
105+
return type == PHP_MRLOOP_TIMER || type == PHP_MRLOOP_FUTURE_TICK ? 0 : 1;
106106
}
107107
static void php_mrloop_add_timer(INTERNAL_FUNCTION_PARAMETERS)
108108
{
@@ -162,6 +162,33 @@ static void php_mrloop_add_periodic_timer(INTERNAL_FUNCTION_PARAMETERS)
162162

163163
return;
164164
}
165+
static void php_mrloop_add_future_tick(INTERNAL_FUNCTION_PARAMETERS)
166+
{
167+
zval *obj;
168+
php_mrloop_cb_t *cb;
169+
php_mrloop_t *this;
170+
zend_fcall_info fci;
171+
zend_fcall_info_cache fci_cache;
172+
173+
fci = empty_fcall_info;
174+
fci_cache = empty_fcall_info_cache;
175+
obj = getThis();
176+
177+
ZEND_PARSE_PARAMETERS_START(1, 1)
178+
Z_PARAM_FUNC(fci, fci_cache)
179+
ZEND_PARSE_PARAMETERS_END();
180+
181+
this = PHP_MRLOOP_OBJ(obj);
182+
cb = emalloc(sizeof(php_mrloop_cb_t));
183+
PHP_CB_TO_MRLOOP_CB(cb, fci, fci_cache);
184+
185+
cb->signal = PHP_MRLOOP_FUTURE_TICK;
186+
cb->data = this->loop;
187+
188+
mr_call_soon(this->loop, php_mrloop_timer_cb, cb);
189+
190+
return;
191+
}
165192

166193
static void php_mrloop_readv_cb(void *data, int res)
167194
{
@@ -261,25 +288,29 @@ static int php_mrloop_tcp_server_recv(void *conn, int fd, ssize_t nbytes, char *
261288
array_init(&args[1]);
262289
add_assoc_string(&args[1], "client_addr", (char *)client->addr);
263290
add_assoc_long(&args[1], "client_port", client->port);
291+
add_assoc_long(&args[1], "client_fd", client->fd);
264292

265293
MRLOOP_G(tcp_cb)->fci.retval = &result;
266294
MRLOOP_G(tcp_cb)->fci.param_count = 2;
267295
MRLOOP_G(tcp_cb)->fci.params = args;
268296

269-
if (zend_call_function(&MRLOOP_G(tcp_cb)->fci, &MRLOOP_G(tcp_cb)->fci_cache) == FAILURE ||
270-
Z_TYPE(result) != IS_STRING)
297+
if (zend_call_function(&MRLOOP_G(tcp_cb)->fci, &MRLOOP_G(tcp_cb)->fci_cache) == FAILURE)
271298
{
272299
PHP_MRLOOP_THROW("There is an error in your callback");
273300
zval_ptr_dtor(&result);
274301

275302
return 1;
276303
}
277304

278-
client->iov.iov_base = Z_STRVAL(result);
279-
client->iov.iov_len = Z_STRLEN(result);
305+
if (Z_TYPE(result) == IS_STRING)
306+
{
307+
client->iov.iov_base = Z_STRVAL(result);
308+
client->iov.iov_len = Z_STRLEN(result);
309+
310+
mr_writev(loop, client->fd, &(client->iov), 1);
311+
mr_flush(loop);
312+
}
280313

281-
mr_writev(loop, client->fd, &(client->iov), 1);
282-
mr_flush(loop);
283314
zval_ptr_dtor(&result);
284315

285316
return 1;
@@ -629,3 +660,36 @@ static void php_mrloop_add_write_stream(INTERNAL_FUNCTION_PARAMETERS)
629660

630661
return;
631662
}
663+
static void php_mrloop_writev(INTERNAL_FUNCTION_PARAMETERS)
664+
{
665+
zend_long fd;
666+
zend_string *contents;
667+
php_iovec_t iov;
668+
php_mrloop_t *this;
669+
zval *obj;
670+
size_t nbytes;
671+
672+
obj = getThis();
673+
674+
ZEND_PARSE_PARAMETERS_START(2, 2)
675+
Z_PARAM_LONG(fd)
676+
Z_PARAM_STR(contents)
677+
ZEND_PARSE_PARAMETERS_END();
678+
679+
this = PHP_MRLOOP_OBJ(obj);
680+
681+
if (fcntl((int)fd, F_GETFD) < 0)
682+
{
683+
PHP_MRLOOP_THROW("Detected invalid file descriptor");
684+
mr_stop(this->loop);
685+
686+
return;
687+
}
688+
689+
nbytes = ZSTR_LEN(contents);
690+
iov.iov_base = ZSTR_VAL(contents);
691+
iov.iov_len = nbytes;
692+
693+
mr_writev(this->loop, fd, &iov, 1);
694+
mr_flush(this->loop);
695+
}

src/loop.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#define DEFAULT_HTTP_HEADER_LIMIT 100
2929
#define PHP_MRLOOP_TIMER 1
3030
#define PHP_MRLOOP_PERIODIC_TIMER 2
31+
#define PHP_MRLOOP_FUTURE_TICK 3
3132

3233
struct php_mrloop_t;
3334
struct php_mrloop_cb_t;
@@ -130,6 +131,8 @@ static int php_mrloop_timer_cb(void *data);
130131
static void php_mrloop_add_timer(INTERNAL_FUNCTION_PARAMETERS);
131132
/* executes a specified action in perpetuity with each successive execution occurring after a specified time interval */
132133
static void php_mrloop_add_periodic_timer(INTERNAL_FUNCTION_PARAMETERS);
134+
/* schedules the execution of a specified action for the next event loop tick */
135+
static void php_mrloop_add_future_tick(INTERNAL_FUNCTION_PARAMETERS);
133136

134137
/* mrloop-bound callback specified during invocation of vectorized read function */
135138
static void php_mrloop_readv_cb(void *data, int res);
@@ -156,6 +159,8 @@ static void php_mrloop_add_signal(INTERNAL_FUNCTION_PARAMETERS);
156159
static void php_mrloop_add_read_stream(INTERNAL_FUNCTION_PARAMETERS);
157160
/* funnels file descriptor in writable stream into event loop and thence executes a non-blocking write operation */
158161
static void php_mrloop_add_write_stream(INTERNAL_FUNCTION_PARAMETERS);
162+
/* performs vectorized non-blocking write operation on a specified file descriptor */
163+
static void php_mrloop_writev(INTERNAL_FUNCTION_PARAMETERS);
159164

160165
zend_class_entry *php_mrloop_ce, *php_mrloop_exception_ce;
161166

tests/011.phpt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
writev() performs vectorized non-blocking write operation on file descriptor
3+
--FILE--
4+
<?php
5+
6+
use ringphp\Mrloop;
7+
8+
$loop = Mrloop::init();
9+
10+
$loop->writev(1, "Hello, user");
11+
$loop->stop();
12+
13+
$loop->run();
14+
15+
?>
16+
--EXPECT--
17+
Hello, user

tests/012.phpt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
writev() throws exception on detection of invalid file descriptor
3+
--FILE--
4+
<?php
5+
6+
use ringphp\Mrloop;
7+
8+
$loop = Mrloop::init();
9+
10+
try {
11+
$loop->writev(987874, "Hello, user");
12+
} catch (\Throwable $err) {
13+
$loop->writev(1, $err->getMessage());
14+
}
15+
$loop->stop();
16+
17+
$loop->run();
18+
19+
?>
20+
--EXPECT--
21+
Detected invalid file descriptor

0 commit comments

Comments
 (0)