]> granicus.if.org Git - php/commitdiff
Refine stream_select() to work with streams that have data in their read
authorWez Furlong <wez@php.net>
Mon, 21 Oct 2002 22:54:37 +0000 (22:54 +0000)
committerWez Furlong <wez@php.net>
Mon, 21 Oct 2002 22:54:37 +0000 (22:54 +0000)
buffers.
When selecting for read, the streams are examined; if any of them have
pending read data, no actual select(2) call is performed; instead the
streams with buffered data are returned; just like a regular select
call.
Prevent erroneous warning in stream_select when obtaining the fd.

ext/standard/file.c
main/php_streams.h
main/streams.c

index bcc11c73654af3897c5ad8e3227c7a37097a54d0..0ef721547898527516142eac32c768ae4b21faf6 100644 (file)
@@ -650,8 +650,12 @@ static int stream_array_to_fd_set(zval *stream_array, fd_set *fds, int *max_fd T
                if (stream == NULL)
                        continue;
 
-               /* get the fd */
-               if (SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)&this_fd, 1)) {
+               /* get the fd.
+                * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
+                * when casting.  It is only used here so that the buffered data warning
+                * is not displayed.
+                * */
+               if (SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL, (void*)&this_fd, 1)) {
                        FD_SET(this_fd, fds);
                        if (this_fd > *max_fd) {
                                *max_fd = this_fd;
@@ -666,7 +670,7 @@ static int stream_array_from_fd_set(zval *stream_array, fd_set *fds TSRMLS_DC)
        zval **elem, **dest_elem;
        php_stream *stream;
        HashTable *new_hash;
-       int this_fd;
+       int this_fd, ret = 0;
 
        if (Z_TYPE_P(stream_array) != IS_ARRAY)
                return 0;
@@ -682,12 +686,18 @@ static int stream_array_from_fd_set(zval *stream_array, fd_set *fds TSRMLS_DC)
                if (stream == NULL)
                        continue;
 
-               /* get the fd */
-               if (SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)&this_fd, 1)) {
+               /* get the fd 
+                * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
+                * when casting.  It is only used here so that the buffered data warning
+                * is not displayed.
+                */
+               if (SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL, (void*)&this_fd, 1)) {
                        if (FD_ISSET(this_fd, fds)) {
                                zend_hash_next_index_insert(new_hash, (void *)elem, sizeof(zval *), (void **)&dest_elem);
                                if (dest_elem)
                                        zval_add_ref(dest_elem);
+                               ret++;
+                               continue;
                        }
                }
        }
@@ -699,7 +709,58 @@ static int stream_array_from_fd_set(zval *stream_array, fd_set *fds TSRMLS_DC)
        zend_hash_internal_pointer_reset(new_hash);
        Z_ARRVAL_P(stream_array) = new_hash;
        
-       return 1;
+       return ret;
+}
+
+static int stream_array_emulate_read_fd_set(zval *stream_array TSRMLS_DC)
+{
+       zval **elem, **dest_elem;
+       php_stream *stream;
+       HashTable *new_hash;
+       int ret = 0;
+
+       if (Z_TYPE_P(stream_array) != IS_ARRAY)
+               return 0;
+
+       ALLOC_HASHTABLE(new_hash);
+       zend_hash_init(new_hash, 0, NULL, ZVAL_PTR_DTOR, 0);
+       
+       for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(stream_array));
+                zend_hash_get_current_data(Z_ARRVAL_P(stream_array), (void **) &elem) == SUCCESS;
+                zend_hash_move_forward(Z_ARRVAL_P(stream_array))) {
+
+               php_stream_from_zval_no_verify(stream, elem);
+               if (stream == NULL)
+                       continue;
+
+               if ((stream->writepos - stream->readpos) > 0) {
+                       /* allow readable non-descriptor based streams to participate in stream_select.
+                        * Non-descriptor streams will only "work" if they have previously buffered the
+                        * data.  Not ideal, but better than nothing.
+                        * This branch of code also allows blocking streams with buffered data to
+                        * operate correctly in stream_select.
+                        * */
+                       zend_hash_next_index_insert(new_hash, (void *)elem, sizeof(zval *), (void **)&dest_elem);
+                       if (dest_elem)
+                               zval_add_ref(dest_elem);
+                       ret++;
+                       continue;
+               }
+       }
+
+       if (ret > 0) {
+               /* destroy old array and add new one */
+               zend_hash_destroy(Z_ARRVAL_P(stream_array));
+               efree(Z_ARRVAL_P(stream_array));
+
+               zend_hash_internal_pointer_reset(new_hash);
+               Z_ARRVAL_P(stream_array) = new_hash;
+       } else {
+               zend_hash_destroy(new_hash);
+               FREE_HASHTABLE(new_hash);
+       }
+       
+       return ret;
 }
 /* }}} */
 
@@ -738,6 +799,17 @@ PHP_FUNCTION(stream_select)
                tv_p = &tv;
        }
 
+       /* slight hack to support buffered data; if there is data sitting in the
+        * read buffer of any of the streams in the read array, let's pretend
+        * that we selected, but return only the readable sockets */
+       if (r_array != NULL) {
+
+               retval = stream_array_emulate_read_fd_set(r_array TSRMLS_CC);
+               if (retval > 0) {
+                       RETURN_LONG(retval);
+               }
+       }
+       
        retval = select(max_fd+1, &rfds, &wfds, &efds, tv_p);
 
        if (retval == -1) {
index 6861671360b95762d8927d9beffa27df85f74b3c..e3b07f785a57e31034b232f831d4df9aa99758c7 100755 (executable)
@@ -455,7 +455,8 @@ PHPAPI php_stream *_php_stream_fopen_temporary_file(const char *dir, const char
 /* try really, really hard to make sure the cast happens (socketpair) */
 #define PHP_STREAM_CAST_TRY_HARD       0x80000000
 #define PHP_STREAM_CAST_RELEASE                0x40000000      /* stream becomes invalid on success */
-#define PHP_STREAM_CAST_MASK           (PHP_STREAM_CAST_TRY_HARD | PHP_STREAM_CAST_RELEASE)
+#define PHP_STREAM_CAST_INTERNAL       0x20000000      /* stream cast for internal use */
+#define PHP_STREAM_CAST_MASK           (PHP_STREAM_CAST_TRY_HARD | PHP_STREAM_CAST_RELEASE | PHP_STREAM_CAST_INTERNAL)
 PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err TSRMLS_DC);
 /* use this to check if a stream can be cast into another form */
 #define php_stream_can_cast(stream, as)        _php_stream_cast((stream), (as), NULL, 0 TSRMLS_CC)
index cab4e2f00182f7e775a2d0b065446a463de3d28c..e288445b3b9ff094832134e365edf385af9fe927 100755 (executable)
@@ -1820,7 +1820,9 @@ PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show
 
 exit_success:
 
-       if ((stream->writepos - stream->readpos) > 0 && stream->fclose_stdiocast != PHP_STREAM_FCLOSE_FOPENCOOKIE) {
+       if ((stream->writepos - stream->readpos) > 0 &&
+                       stream->fclose_stdiocast != PHP_STREAM_FCLOSE_FOPENCOOKIE &&
+                       (flags & PHP_STREAM_CAST_INTERNAL) == 0) {
                /* the data we have buffered will be lost to the third party library that
                 * will be accessing the stream.  Emit a warning so that the end-user will
                 * know that they should try something else */