1 /*
2  +----------------------------------------------------------------------+
3  | Swoole                                                               |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 2012-2018 The Swoole Group                             |
6  +----------------------------------------------------------------------+
7  | This source file is subject to version 2.0 of the Apache license,    |
8  | that is bundled with this package in the file LICENSE, and is        |
9  | available through the world-wide-web at the following url:           |
10  | http://www.apache.org/licenses/LICENSE-2.0.html                      |
11  | If you did not receive a copy of the Apache2.0 license and are unable|
12  | to obtain it through the world-wide-web, please send a note to       |
13  | license@swoole.com so we can mail you a copy immediately.            |
14  +----------------------------------------------------------------------+
15  | Author: Xinyu Zhu  <xyzhu1120@gmail.com>                             |
16  |         Tianfeng Han <rango@swoole.com>                              |
17  +----------------------------------------------------------------------+
18  */
19 
20 #include "php_swoole_cxx.h"
21 
22 #include "swoole_coroutine_channel.h"
23 
24 using swoole::coroutine::Channel;
25 
26 static zend_class_entry *swoole_channel_coro_ce;
27 static zend_object_handlers swoole_channel_coro_handlers;
28 
29 struct ChannelObject {
30     Channel *chan;
31     zend_object std;
32 };
33 
34 SW_EXTERN_C_BEGIN
35 static PHP_METHOD(swoole_channel_coro, __construct);
36 static PHP_METHOD(swoole_channel_coro, push);
37 static PHP_METHOD(swoole_channel_coro, pop);
38 static PHP_METHOD(swoole_channel_coro, close);
39 static PHP_METHOD(swoole_channel_coro, stats);
40 static PHP_METHOD(swoole_channel_coro, length);
41 static PHP_METHOD(swoole_channel_coro, isEmpty);
42 static PHP_METHOD(swoole_channel_coro, isFull);
43 SW_EXTERN_C_END
44 
45 // clang-format off
46 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_channel_coro_construct, 0, 0, 0)
47     ZEND_ARG_INFO(0, size)
48 ZEND_END_ARG_INFO()
49 
50 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_channel_coro_push, 0, 0, 1)
51     ZEND_ARG_INFO(0, data)
52     ZEND_ARG_INFO(0, timeout)
53 ZEND_END_ARG_INFO()
54 
55 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_channel_coro_pop, 0, 0, 0)
56     ZEND_ARG_INFO(0, timeout)
57 ZEND_END_ARG_INFO()
58 
59 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0)
60 ZEND_END_ARG_INFO()
61 
62 static const zend_function_entry swoole_channel_coro_methods[] =
63 {
64     PHP_ME(swoole_channel_coro, __construct, arginfo_swoole_channel_coro_construct, ZEND_ACC_PUBLIC)
65     PHP_ME(swoole_channel_coro, push, arginfo_swoole_channel_coro_push, ZEND_ACC_PUBLIC)
66     PHP_ME(swoole_channel_coro, pop,  arginfo_swoole_channel_coro_pop,  ZEND_ACC_PUBLIC)
67     PHP_ME(swoole_channel_coro, isEmpty, arginfo_swoole_void, ZEND_ACC_PUBLIC)
68     PHP_ME(swoole_channel_coro, isFull, arginfo_swoole_void, ZEND_ACC_PUBLIC)
69     PHP_ME(swoole_channel_coro, close, arginfo_swoole_void, ZEND_ACC_PUBLIC)
70     PHP_ME(swoole_channel_coro, stats, arginfo_swoole_void, ZEND_ACC_PUBLIC)
71     PHP_ME(swoole_channel_coro, length, arginfo_swoole_void, ZEND_ACC_PUBLIC)
72     PHP_FE_END
73 };
74 // clang-format on
75 
php_swoole_channel_coro_fetch_object(zend_object * obj)76 static sw_inline ChannelObject *php_swoole_channel_coro_fetch_object(zend_object *obj) {
77     return (ChannelObject *) ((char *) obj - swoole_channel_coro_handlers.offset);
78 }
79 
php_swoole_get_channel(zval * zobject)80 static sw_inline Channel *php_swoole_get_channel(zval *zobject) {
81     Channel *chan = php_swoole_channel_coro_fetch_object(Z_OBJ_P(zobject))->chan;
82     if (UNEXPECTED(!chan)) {
83         php_swoole_fatal_error(E_ERROR, "you must call Channel constructor first");
84     }
85     return chan;
86 }
87 
php_swoole_channel_coro_dtor_object(zend_object * object)88 static void php_swoole_channel_coro_dtor_object(zend_object *object) {
89     zend_objects_destroy_object(object);
90 
91     ChannelObject *chan_object = php_swoole_channel_coro_fetch_object(object);
92     Channel *chan = chan_object->chan;
93     if (chan) {
94         zval *data;
95         while ((data = (zval *) chan->pop_data())) {
96             sw_zval_free(data);
97         }
98         delete chan;
99         chan_object->chan = nullptr;
100     }
101 }
102 
php_swoole_channel_coro_free_object(zend_object * object)103 static void php_swoole_channel_coro_free_object(zend_object *object) {
104     ChannelObject *chan_object = php_swoole_channel_coro_fetch_object(object);
105     Channel *chan = chan_object->chan;
106     if (chan) {
107         delete chan;
108     }
109     zend_object_std_dtor(object);
110 }
111 
php_swoole_channel_coro_create_object(zend_class_entry * ce)112 static zend_object *php_swoole_channel_coro_create_object(zend_class_entry *ce) {
113     ChannelObject *chan_object = (ChannelObject *) zend_object_alloc(sizeof(ChannelObject), ce);
114     zend_object_std_init(&chan_object->std, ce);
115     object_properties_init(&chan_object->std, ce);
116     chan_object->std.handlers = &swoole_channel_coro_handlers;
117     return &chan_object->std;
118 }
119 
php_swoole_channel_coro_minit(int module_number)120 void php_swoole_channel_coro_minit(int module_number) {
121     SW_INIT_CLASS_ENTRY(
122         swoole_channel_coro, "Swoole\\Coroutine\\Channel", nullptr, "Co\\Channel", swoole_channel_coro_methods);
123     SW_SET_CLASS_NOT_SERIALIZABLE(swoole_channel_coro);
124     SW_SET_CLASS_CLONEABLE(swoole_channel_coro, sw_zend_class_clone_deny);
125     SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_channel_coro, sw_zend_class_unset_property_deny);
126     SW_SET_CLASS_CUSTOM_OBJECT(swoole_channel_coro,
127                                php_swoole_channel_coro_create_object,
128                                php_swoole_channel_coro_free_object,
129                                ChannelObject,
130                                std);
131     SW_SET_CLASS_DTOR(swoole_channel_coro, php_swoole_channel_coro_dtor_object);
132     if (SWOOLE_G(use_shortname)) {
133         SW_CLASS_ALIAS("Chan", swoole_channel_coro);
134     }
135 
136     zend_declare_property_long(swoole_channel_coro_ce, ZEND_STRL("capacity"), 0, ZEND_ACC_PUBLIC);
137     zend_declare_property_long(swoole_channel_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC);
138 
139     SW_REGISTER_LONG_CONSTANT("SWOOLE_CHANNEL_OK", Channel::ERROR_OK);
140     SW_REGISTER_LONG_CONSTANT("SWOOLE_CHANNEL_TIMEOUT", Channel::ERROR_TIMEOUT);
141     SW_REGISTER_LONG_CONSTANT("SWOOLE_CHANNEL_CLOSED", Channel::ERROR_CLOSED);
142     SW_REGISTER_LONG_CONSTANT("SWOOLE_CHANNEL_CANCELED", Channel::ERROR_CANCELED);
143 }
144 
PHP_METHOD(swoole_channel_coro,__construct)145 static PHP_METHOD(swoole_channel_coro, __construct) {
146     zend_long capacity = 1;
147 
148     ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1)
149     Z_PARAM_OPTIONAL
150     Z_PARAM_LONG(capacity)
151     ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
152 
153     if (capacity <= 0) {
154         capacity = 1;
155     }
156 
157     ChannelObject *chan_t = php_swoole_channel_coro_fetch_object(Z_OBJ_P(ZEND_THIS));
158     chan_t->chan = new Channel(capacity);
159     zend_update_property_long(swoole_channel_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("capacity"), capacity);
160 }
161 
PHP_METHOD(swoole_channel_coro,push)162 static PHP_METHOD(swoole_channel_coro, push) {
163     Channel *chan = php_swoole_get_channel(ZEND_THIS);
164     zval *zdata;
165     double timeout = -1;
166 
167     ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 2)
168     Z_PARAM_ZVAL(zdata)
169     Z_PARAM_OPTIONAL
170     Z_PARAM_DOUBLE(timeout)
171     ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
172 
173     Z_TRY_ADDREF_P(zdata);
174     zdata = sw_zval_dup(zdata);
175     if (chan->push(zdata, timeout)) {
176         zend_update_property_long(
177             swoole_channel_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), Channel::ERROR_OK);
178         RETURN_TRUE;
179     } else {
180         zend_update_property_long(
181             swoole_channel_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), chan->get_error());
182         Z_TRY_DELREF_P(zdata);
183         efree(zdata);
184         RETURN_FALSE;
185     }
186 }
187 
PHP_METHOD(swoole_channel_coro,pop)188 static PHP_METHOD(swoole_channel_coro, pop) {
189     Channel *chan = php_swoole_get_channel(ZEND_THIS);
190     double timeout = -1;
191 
192     ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1)
193     Z_PARAM_OPTIONAL
194     Z_PARAM_DOUBLE(timeout)
195     ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
196 
197     zval *zdata = (zval *) chan->pop(timeout);
198     if (zdata) {
199         RETVAL_ZVAL(zdata, 0, 0);
200         efree(zdata);
201         zend_update_property_long(
202             swoole_channel_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), Channel::ERROR_OK);
203     } else {
204         zend_update_property_long(
205             swoole_channel_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), chan->get_error());
206         RETURN_FALSE;
207     }
208 }
209 
PHP_METHOD(swoole_channel_coro,close)210 static PHP_METHOD(swoole_channel_coro, close) {
211     Channel *chan = php_swoole_get_channel(ZEND_THIS);
212     RETURN_BOOL(chan->close());
213 }
214 
PHP_METHOD(swoole_channel_coro,length)215 static PHP_METHOD(swoole_channel_coro, length) {
216     Channel *chan = php_swoole_get_channel(ZEND_THIS);
217     RETURN_LONG(chan->length());
218 }
219 
PHP_METHOD(swoole_channel_coro,isEmpty)220 static PHP_METHOD(swoole_channel_coro, isEmpty) {
221     Channel *chan = php_swoole_get_channel(ZEND_THIS);
222     RETURN_BOOL(chan->is_empty());
223 }
224 
PHP_METHOD(swoole_channel_coro,isFull)225 static PHP_METHOD(swoole_channel_coro, isFull) {
226     Channel *chan = php_swoole_get_channel(ZEND_THIS);
227     RETURN_BOOL(chan->is_full());
228 }
229 
PHP_METHOD(swoole_channel_coro,stats)230 static PHP_METHOD(swoole_channel_coro, stats) {
231     Channel *chan = php_swoole_get_channel(ZEND_THIS);
232     array_init(return_value);
233     add_assoc_long_ex(return_value, ZEND_STRL("consumer_num"), chan->consumer_num());
234     add_assoc_long_ex(return_value, ZEND_STRL("producer_num"), chan->producer_num());
235     add_assoc_long_ex(return_value, ZEND_STRL("queue_num"), chan->length());
236 }
237