Permalink
Browse files

in response to issues raised in #75 ::isTerminated

  • Loading branch information...
1 parent 2e2ce26 commit 2f2a9d14f1fead56b165e41856a625b832eef4a6 @krakjoe committed Mar 20, 2013
View
@@ -21,6 +21,7 @@ PHP_METHOD(Stackable, wait);
PHP_METHOD(Stackable, notify);
PHP_METHOD(Stackable, isRunning);
PHP_METHOD(Stackable, isWaiting);
+PHP_METHOD(Stackable, isTerminated);
PHP_METHOD(Stackable, synchronized);
PHP_METHOD(Stackable, lock);
PHP_METHOD(Stackable, unlock);
@@ -37,6 +38,8 @@ ZEND_BEGIN_ARG_INFO_EX(Stackable_isRunning, 0, 0, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(Stackable_isWaiting, 0, 0, 0)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_INFO_EX(Stackable_isTerminated, 0, 0, 0)
+ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(Stackable_synchronized, 0, 0, 1)
ZEND_ARG_INFO(0, function)
@@ -58,6 +61,7 @@ zend_function_entry pthreads_stackable_methods[] = {
PHP_ME(Stackable, notify, Stackable_notify, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
PHP_ME(Stackable, isRunning, Stackable_isRunning, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
PHP_ME(Stackable, isWaiting, Stackable_isWaiting, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
+ PHP_ME(Stackable, isTerminated, Stackable_isTerminated, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
PHP_ME(Stackable, synchronized, Stackable_synchronized, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
PHP_ME(Stackable, lock, Stackable_lock, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
PHP_ME(Stackable, unlock, Stackable_unlock, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
@@ -113,6 +117,17 @@ PHP_METHOD(Stackable, isWaiting)
} else zend_error(E_ERROR, "pthreads has experienced an internal error while preparing to read the state of a %s and cannot continue", PTHREADS_NAME);
} /* }}} */
+/* {{{ proto boolean Stackable::isTerminated()
+ Will return true if the referenced Stackable suffered fatal errors or uncaught exceptions */
+PHP_METHOD(Stackable, isTerminated)
+{
+ PTHREAD thread = PTHREADS_FETCH;
+
+ if (thread) {
+ RETURN_BOOL(pthreads_state_isset(thread->state, PTHREADS_ST_ERROR TSRMLS_CC));
+ } else zend_error(E_ERROR, "pthreads has experienced an internal error while preparing to read the state of a %s and cannot continue", PTHREADS_NAME);
+} /* }}} */
+
/* {{{ proto void Stackable::synchronized(Callable function, ...)
Will synchronize the object, call the function, passing anything after the function as parameters
*/
View
@@ -25,6 +25,7 @@ PHP_METHOD(Thread, isStarted);
PHP_METHOD(Thread, isRunning);
PHP_METHOD(Thread, isJoined);
PHP_METHOD(Thread, isWaiting);
+PHP_METHOD(Thread, isTerminated);
PHP_METHOD(Thread, getThreadId);
PHP_METHOD(Thread, getCreatorId);
@@ -66,6 +67,9 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(Thread_isWaiting, 0, 0, 0)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_INFO_EX(Thread_isTerminated, 0, 0, 0)
+ZEND_END_ARG_INFO()
+
ZEND_BEGIN_ARG_INFO_EX(Thread_synchronized, 0, 0, 1)
ZEND_ARG_INFO(0, function)
ZEND_END_ARG_INFO()
@@ -90,6 +94,7 @@ zend_function_entry pthreads_thread_methods[] = {
PHP_ME(Thread, isRunning, Thread_isRunning, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
PHP_ME(Thread, isJoined, Thread_isJoined, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
PHP_ME(Thread, isWaiting, Thread_isWaiting, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
+ PHP_ME(Thread, isTerminated, Thread_isTerminated, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
PHP_ME(Thread, getThreadId, Thread_getThreadId, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL|ZEND_ACC_STATIC)
PHP_ME(Thread, getCreatorId, Thread_getCreatorId, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
PHP_ME(Thread, synchronized, Thread_synchronized, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
@@ -178,6 +183,17 @@ PHP_METHOD(Thread, isWaiting)
} else zend_error(E_ERROR, "pthreads has experienced an internal error while preparing to read the state of a %s and cannot continue", PTHREADS_NAME);
} /* }}} */
+/* {{{ proto boolean Thread::isTerminated()
+ Will return true if the referenced thread suffered fatal errors or uncaught exceptions */
+PHP_METHOD(Thread, isTerminated)
+{
+ PTHREAD thread = PTHREADS_FETCH;
+
+ if (thread) {
+ RETURN_BOOL(pthreads_state_isset(thread->state, PTHREADS_ST_ERROR TSRMLS_CC));
+ } else zend_error(E_ERROR, "pthreads has experienced an internal error while preparing to read the state of a %s and cannot continue", PTHREADS_NAME);
+} /* }}} */
+
/* {{{ proto boolean Thread::wait([long timeout])
Will cause the calling thread to wait for notification from the referenced object
When a timeout is used and reached boolean false will return
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- $Revision$ -->
+
+<refentry xml:id="stackable.isterminated" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <refnamediv>
+ <refname>Stackable::isTerminated</refname>
+ <refpurpose>State Detection</refpurpose>
+ </refnamediv>
+
+ <refsect1 role="description">
+ &reftitle.description;
+ <methodsynopsis>
+ <modifier>final</modifier> <modifier>public</modifier> <type>boolean</type><methodname>Stackable::isTerminated</methodname>
+ <void />
+ </methodsynopsis>
+ <para>
+ Tell if the referenced Stackable suffered uncaught exceptions or fatal errors during execution
+ </para>
+ </refsect1>
+
+ <refsect1 role="parameters">
+ &reftitle.parameters;
+ &no.function.parameters;
+ </refsect1>
+
+ <refsect1 role="returnvalues">
+ &reftitle.returnvalues;
+ <para>
+ A boolean indication of state
+ </para>
+ </refsect1>
+
+ <refsect1 role="examples">
+ &reftitle.examples;
+ <para>
+ <example>
+ <title>Detect the state of the referenced Stackable</title>
+ <programlisting role="php">
+<![CDATA[
+<?php
+class Work extends Stackable {
+ public function run() {
+ i_do_not_exist();
+ }
+}
+
+class My extends Worker {
+ public function run() {
+ /** ... **/
+ }
+}
+
+$my = new My();
+$work = new Work();
+$my->stack($work);
+$my->start();
+$my->shutdown();
+
+var_dump($work->isTerminated());
+?>
+]]>
+ </programlisting>
+ &example.outputs;
+ <screen>
+<![CDATA[
+bool(true)
+]]>
+ </screen>
+ </example>
+ </para>
+ </refsect1>
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+indent-tabs-mode:nil
+sgml-parent-document:nil
+sgml-default-dtd-file:"~/.phpdoc/manual.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+vim600: syn=xml fen fdm=syntax fdl=2 si
+vim: et tw=78 syn=sgml
+vi: ts=1 sw=1
+-->
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- $Revision$ -->
+
+<refentry xml:id="thread.isterminated" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <refnamediv>
+ <refname>Thread::isTerminated</refname>
+ <refpurpose>State Detection</refpurpose>
+ </refnamediv>
+
+ <refsect1 role="description">
+ &reftitle.description;
+ <methodsynopsis>
+ <modifier>final</modifier> <modifier>public</modifier> <type>boolean</type><methodname>Thread::isTerminated</methodname>
+ <void />
+ </methodsynopsis>
+ <para>
+ Tell if the referenced Thread suffered fatal errors or uncaught exceptions during execution
+ </para>
+ </refsect1>
+
+ <refsect1 role="parameters">
+ &reftitle.parameters;
+ &no.function.parameters;
+ </refsect1>
+
+ <refsect1 role="returnvalues">
+ &reftitle.returnvalues;
+ <para>
+ A boolean indication of state
+ </para>
+ </refsect1>
+
+ <refsect1 role="examples">
+ &reftitle.examples;
+ <para>
+ <example>
+ <title>Detect the state of the referenced Thread</title>
+ <programlisting role="php">
+<![CDATA[
+<?php
+class My extends Thread {
+ public function run() {
+ i_do_not_exist();
+ }
+}
+$my = new My();
+$my->start();
+$my->join();
+var_dump($my->isTerminated());
+?>
+]]>
+ </programlisting>
+ &example.outputs;
+ <screen>
+<![CDATA[
+bool(true)
+]]>
+ </screen>
+ </example>
+ </para>
+ </refsect1>
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+indent-tabs-mode:nil
+sgml-parent-document:nil
+sgml-default-dtd-file:"~/.phpdoc/manual.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+vim600: syn=xml fen fdm=syntax fdl=2 si
+vim: et tw=78 syn=sgml
+vi: ts=1 sw=1
+-->
View
@@ -15,6 +15,7 @@
<function name='thread::isrunning' from='PECL pthreads &gt;= 0.34'/>
<function name='thread::isjoined' from='PECL pthreads &gt;= 0.34'/>
<function name='thread::iswaiting' from='PECL pthreads &gt;= 0.34'/>
+ <function name='thread::isterminated' from='PECL pthreads &gt;= 0.43'/>
<function name='thread::getthreadid' from='PECL pthreads &gt;= 0.34'/>
<function name='thread::getcreatorid' from='PECL pthreads &gt;= 0.36'/>
<function name='thread::synchronized' from='PECL pthreads &gt;= 0.40'/>
@@ -35,6 +36,7 @@
<function name='stackable::notify' from='PECL pthreads &gt;= 0.36'/>
<function name='stackable::isrunning' from='PECL pthreads &gt;= 0.36'/>
<function name='stackable::iswaiting' from='PECL pthreads &gt;= 0.36'/>
+ <function name='stackable::isterminated' from='PECL pthreads &gt;= 0.43'/>
<function name='stackable::synchronized' from='PECL pthreads &gt;= 0.40'/>
<function name='stackable::lock' from='PECL pthreads &gt;= 0.40'/>
<function name='stackable::unlock' from='PECL pthreads &gt;= 0.40'/>
View
@@ -701,15 +701,26 @@ static void * pthreads_routine(void *arg) {
/* call the function */
pthreads_state_set(current->state, PTHREADS_ST_RUNNING TSRMLS_CC);
{
+ zend_bool terminated = 0;
/* graceful fatalities */
zend_try {
+ /* ::run */
zend_call_function(&info, &cache TSRMLS_CC);
} zend_catch {
- /* catch errors */
+ /* catches fatal errors and uncaught exceptions */
+ terminated = 1;
} zend_end_try();
- if (current)
- pthreads_state_unset(current->state, PTHREADS_ST_RUNNING TSRMLS_CC);
+ if (current) {
+ /* set terminated state */
+ if (terminated) {
+ pthreads_state_set(
+ current->state, PTHREADS_ST_ERROR TSRMLS_CC);
+ }
+
+ /* unset running for waiters */
+ pthreads_state_unset(current->state, PTHREADS_ST_RUNNING TSRMLS_CC);
+ }
}
#if PHP_VERSION_ID > 50399
{
View
@@ -22,6 +22,7 @@
#define PTHREADS_ST_RUNNING 2
#define PTHREADS_ST_WAITING 4
#define PTHREADS_ST_JOINED 8
+#define PTHREADS_ST_ERROR 16
#ifndef HAVE_PTHREADS_H
# include <src/pthreads.h>
@@ -0,0 +1,18 @@
+--TEST--
+Test stateful fatalities
+--DESCRIPTION--
+This test verifies that state includes fatalities
+--FILE--
+<?php
+class TestThread extends Thread {
+ public function run(){
+ @i_do_not_exist();
+ }
+}
+$test = new TestThread();
+$test->start();
+$test->join();
+var_dump($test->isTerminated());
+?>
+--EXPECTF--
+bool(true)
View
@@ -14,17 +14,25 @@ class ThreadTest extends Thread {
protected function isSent($flag = null) {
if ($flag === null) {
return $this->sent;
- } else return ($this->sent = $this->notify());
+ } else {
+ $this->sent = $flag;
+ $this->synchronized(function($me){
+ $me->notify();
+ }, $this);
+ return $flag;
+ }
}
}
$thread = new ThreadTest();
if($thread->start()) {
/*
you only ever wait FOR something !
*/
- if (!$thread->isSent()) {
- var_dump($thread->wait());
- } else printf("bool(true)\n");
+ $thread->synchronized(function($me){
+ if (!$me->isSent()) {
+ var_dump($me->wait());
+ } else printf("bool(true)\n");
+ }, $thread);
/* note that: this works because of protection */
/* without protection, notify in the other thread can cause the process to continue */
@@ -36,4 +44,4 @@ if($thread->start()) {
?>
--EXPECT--
bool(true)
-bool(true)
+bool(true)

0 comments on commit 2f2a9d1

Please sign in to comment.