Git Product home page Git Product logo

phactor's People

Contributors

tpunt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

phactor's Issues

Project Architecture and Definition

Hi @tpunt, hope you're going well!

First of all, you're the man, this is an awesome project that was in my mind since two weeks and I came from a conference last week thinking in implement this in PHP, so I'd love to help you in this project!

Second, I was looking at the source code and it is sound a bit confusing for me yet. I'm working with Actor Model using Scala (Akka) and I was thinking how would we implement the runtime architecture. I mean, in Scala w/ Akka, when we run the application, we start it like a daemon (using the internal Akka Http Server or Netty server) and I was thinking how we would architect the runtime of a PHP app using the Actor Model? TCP Sockets? PHP Built-in Web Server? ReactPHP

There are some other things to care about, like clustering the application, we may need a node coordinator, a sharding model, a persistence model and etc...

Again, I would love to help you in this journey as I'm loving work with Actor Model in Scala and this would be a good step in architecture point-of-view of PHP apps.

If is good for you, we can make a discussion group to take this project to a next level.

Let me know what you think about this, ok?

Best regards!

Creating an ActorSystem freezes in Alpine 3.8

Hi, I messaged you a few days ago on Reddit about this bug. I've pinned down the freezing to the moment an ActorSystem is instantiated:

<?php

echo "123\n";
$as = new phactor\ActorSystem();
echo "456\n";

outputs:

123

I've put together a Dockerfile with an environment that reproduces the issue, if that helps (I've also recorded its build log, the logs relevant to phactor start at L903, though I see nothing wrong).

Avoid realloc call in context switching

Currently, realloc is invoked every time the machine stack is saved. If two variables were used to keep track of the allocated machine stack size and the currently used stack size, then a realloc call would only need to be incurred if used_stack_size > allocated_stack_size.

A POC can be seen below, but it does not work (I haven't had time to debug why, yet):

diff --git a/src/ph_context.c b/src/ph_context.c
index e3088ee9e2..4e7e47543f 100644
--- a/src/ph_context.c
+++ b/src/ph_context.c
@@ -23,11 +23,12 @@
 void ph_mcontext_init(ph_mcontext_t *mc, void (*cb)(void))
 {
 #ifdef PH_FIXED_STACK_SIZE
-    mc->stack_size = PH_FIXED_STACK_SIZE;
-    mc->stack_space = calloc(1, mc->stack_size + STACK_ALIGNMENT - 1);
+    mc->allocated_stack_size = PH_FIXED_STACK_SIZE;
+    mc->stack_space = calloc(1, mc->allocated_stack_size + STACK_ALIGNMENT - 1);
     mc->aligned_stack_space = (void *)((uintptr_t)mc->stack_space + (STACK_ALIGNMENT - 1) & ~(STACK_ALIGNMENT - 1));
 #else
-    mc->stack_size = 0;
+    mc->allocated_stack_size = 0;
+    mc->used_stack_size = 0;
     mc->stack_space = NULL;
 #endif
     mc->cb = cb;
@@ -43,8 +44,8 @@ void ph_mcontext_reset(ph_mcontext_t *mc)
     mc->started = 0;
 
     // assumes the stack always grows downwards
-    mc->rbp = mc->aligned_stack_space + mc->stack_size;
-    mc->rsp = mc->aligned_stack_space + mc->stack_size;
+    mc->rbp = mc->aligned_stack_space + mc->allocated_stack_size;
+    mc->rsp = mc->aligned_stack_space + mc->allocated_stack_size;
 #endif
 }
 
diff --git a/src/ph_context.h b/src/ph_context.h
index d494c577da..875cd409ad 100644
--- a/src/ph_context.h
+++ b/src/ph_context.h
@@ -35,8 +35,10 @@ typedef struct _ph_mcontext_t {
     void *r15; // 80
     void *stack_space; // 88
     void (*cb)(void); // 96
-    int stack_size; // 104
-#ifdef PH_FIXED_STACK_SIZE
+    int allocated_stack_size; // 104
+#ifndef PH_FIXED_STACK_SIZE
+    int used_stack_size; // 108
+#else
     int started; // 108
     void *aligned_stack_space; // 112
 #endif
diff --git a/src/ph_context_switch.S b/src/ph_context_switch.S
index 1f5bdc09e6..ccf9b80cf9 100644
--- a/src/ph_context_switch.S
+++ b/src/ph_context_switch.S
@@ -256,11 +256,11 @@ FN(ph_mcontext_resume)
     movq %r15, 80(%rdi)
 
     ## restore the old context
-    subq 104(%rsi), %rsp
+    subq 108(%rsi), %rsp
 
     movq %rsp, %rdi
     movq %rsi, %rbx
-    movq 104(%rsi), %rdx
+    movq 108(%rsi), %rdx
     movq 88(%rsi), %rsi
     callq CALL(memcpy)
     movq %rbx, %rsi
@@ -285,23 +285,28 @@ FN(ph_mcontext_interrupt)
     pushq %rbp
     movq %rsp, %rbp
 
-    movq 16(%rsi), %rax      ## calculate stack size (old_rsp - current_rsp)
+    movq 16(%rsi), %rax      ## calculate used stack size (old_rsp - current_rsp)
     subq %rsp, %rax
-    movq %rax, 104(%rdi)
+    movq %rax, 108(%rdi)
 
     pushq %rdi
     pushq %rsi
 
-    movq 104(%rdi), %rsi
+    cmpq 104(%rdi), %rax     ## if (used_stack_size > allocated_stack_size)
+    jg L6
+
+    movq 108(%rdi), %rsi
+    movq %rsi, 104(%rdi)     ## allocated_stack_size = used_stack_size
     movq 88(%rdi), %rdi
     callq CALL(realloc)
     movq 0(%rsp), %rsi
     movq 8(%rsp), %rdi
     movq %rax, 88(%rdi)
 
+L6:
     movq %rsp, %rsi
     addq $16, %rsi           ## skip past 2 pushes onto stack
-    movq 104(%rdi), %rdx
+    movq 108(%rdi), %rdx
     movq 88(%rdi), %rdi
     callq CALL(memcpy)

/root/pht/src/pht_copy.c:784: copy_zval_table: Assertion `0' failed

I compiled PHP using this commands:

#!/bin/sh
git clone https://github.com/php/php-src.git -b PHP-7.2.27 --depth=1 php-src-7.2
cd php-src-7.2
rm -rf /etc/php7

./buildconf --force

./configure --prefix=/etc/php7 --with-bz2 --with-zlib --enable-zip --disable-cgi \
   --enable-soap --enable-intl --with-mcrypt --with-openssl --with-readline --with-curl \
   --enable-ftp --enable-mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd \
   --enable-sockets --enable-pcntl --with-pspell --with-enchant --with-gettext \
   --with-gd --enable-exif --with-jpeg-dir --with-png-dir --with-freetype-dir --with-xsl \
   --enable-bcmath --enable-mbstring --enable-calendar --enable-simplexml --enable-json \
   --enable-hash --enable-session --enable-xml --enable-wddx --enable-opcache \
   --with-pcre-regex --with-config-file-path=/etc/php7/cli \
   --with-config-file-scan-dir=/etc/php7/etc --enable-cli --enable-maintainer-zts \
   --with-tsrm-pthreads --enable-debug --enable-fpm \
   --with-fpm-user=www-data --with-fpm-group=www-data

make && make install

chmod o+x /etc/php7/bin/phpize
chmod o+x /etc/php7/bin/php-config

mkdir /etc/php7/cli
cp php.ini-production /etc/php7/cli/php.ini

sed -e '924iextension=/etc/php7/lib/php/extensions/debug-zts-20170718/pht.so' -i /etc/php7/cli/php.ini

update-alternatives --install /usr/bin/php php /etc/php7/bin/php 74
update-alternatives --install /usr/bin/phpize phpize /etc/php7/bin/phpize 99

And then, I installed pht:

cd ~
rm -rf pht
git clone https://github.com/tpunt/pht
cd pht
git checkout tags/v0.0.1
phpize
./configure --prefix=/etc/php7 --with-libdir=/lib/x86_64-linux-gnu --enable-pthreads=shared --with-php-config=/etc/php7/bin/php-config
make
make install

Then I runned the command php script.php this error prompted:

/root/pht/src/pht_copy.c:784: copy_zval_table: Assertion 0' failed`

https://github.com/tpunt/phactor/blob/master/src/ph_copy.c#L784

PHP Version: 7.2.27

I need to use a lower PHP version?

EDIT: I tried version 7.2.4 (from March 2018, the latest commit on this repo was April 2018)

root@debian:/var/www/api.z3nth10n.net# php -v
PHP 7.2.4-dev (cli) (built: Jan 23 2020 11:01:58) ( ZTS DEBUG )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies

But this is still not working... :(

EDIT2: I edited the line to return NULL; by the moment.

Supervision Trees

Work on supervision trees has begun in the supervision-trees branch. The API I currently have in mind looks as follows:

final class Supervisor
{
    public const ONE_FOR_ONE = 0;

    public function __construct(ActorRef|string $supervisor[, int $strategy]);

    // Add a pre-existing actor to the group of supervised workers
    public function addWorker(ActorRef|string $worker) : void;

    // Creates a new actor, links it to the supervisor, and return its actor reference
    // This should be used if the actor's constructor may throw an exception
    public function newWorker(string $actorClass[, array $ctorArgs[, string $actorName]]) : ActorRef;
}

To do list:

  • Prevent the supervision tree from containing cycles
  • Work out what the semantics should be for a crashing supervisor

Change the initially allocated VM stack size for each actor

Currently, the VM will allocate a new stack of 262144 bytes (256kb). This makes actors very expensive in terms of memory usage. The default stack allocation size should be lowered, since the stack sizes for actors will generally be much lower than seen in a typical PHP application.

I have a patch for this, but it causes heap corruption issues for some reason:

diff --git a/src/classes/actor.c b/src/classes/actor.c
index 4a410c5257..4b2ccb70a4 100644
--- a/src/classes/actor.c
+++ b/src/classes/actor.c
@@ -311,7 +311,16 @@ zend_object* phactor_actor_ctor(zend_class_entry *entry)
 
     ph_mcontext_init(&actor_internal->context.mc, process_message_handler);
 
-    zend_vm_stack_init();
+    zend_vm_stack page = (zend_vm_stack)emalloc(PH_VM_STACK_SIZE);
+    page->top = ZEND_VM_STACK_ELEMENTS(page);
+    page->end = (zval*)((char*)page + PH_VM_STACK_SIZE);
+    page->prev = NULL;
+
+    EG(vm_stack) = page;
+    EG(vm_stack)->top++;
+    EG(vm_stack_top) = EG(vm_stack)->top;
+    EG(vm_stack_end) = EG(vm_stack)->end;
+
     ph_vmcontext_get(&actor_internal->context.vmc);
 
     /*
diff --git a/src/ph_context.h b/src/ph_context.h
index d494c577da..249b269d45 100644
--- a/src/ph_context.h
+++ b/src/ph_context.h
@@ -53,6 +53,8 @@ typedef struct _ph_context_t {
     ph_vmcontext_t vmc;
 } ph_context_t;
 
+#define PH_VM_STACK_SIZE 256 // starting size of PHP's VM stack
+
 #ifdef PH_FIXED_STACK_SIZE
 # define STACK_ALIGNMENT 16 // Ensure 16 byte stack alignment (for OS X)

Strangely enough, 256 bytes causes a few test failures (such as the actor-cleanup-on-shutdown.phpt test) with a heap corruption issue, and 512 bytes causes the context-switching-basics3.phpt test to fail with a segfault. Using 64, 128, 1024, 2048, etc, bytes seems to work fine. This needs some investigation before it gets changed.

Serialising closures in arrays

Closures cannot be serialised inside of arrays. This is because arrays are simply serialised with PHP's internal serialiser. Closures require special handling with serialisation, and so arrays will need to be serialised differently if this is going to work. The ph_hashtable_t type should be reusable for this, so it should be a matter of mapping PHP's hash table to phactor's hash table.

Example:

<?php

use phactor\{ActorSystem, Actor, ActorRef};

$as = new ActorSystem(1);

class A extends Actor
{
    public function __construct(array $a){}
    public function receive(){}
}


$a = new ActorRef(A::class, [[function(){}]], 'a');

ActorSystem::shutdown();

ActorSystem and autoloading

To perform autoloading in the actor system, an autoloader should be accepted by the actor system constructor (I don't see other cleaner ways to do this).

The autoloader (for now) could just be a file name as the second parameter to ActorSystem::__construct, where the file will be executed by the individually spawned threads on actor system startup.

Internal Non-Blocking APIs

Work has begun on the internal non-blocking APIs (see the non-blocking-io branch).

These APIs will allow for the automatic interruption of an actor when an IO operation is performed, where the actor will be placed into a blocked state. Once the IO operation has been performed, the actor will be rescheduled to continue its execution. This control flow should be seamless to the developer, and will enable for CPU usage to be maximised by allowing for other actors to continue executing (when previously the thread would be blocked by IO-blocking operations).

libuv is being utilised for these APIs. I have not yet decided whether to bundle it in with this extension's source code, or make it as another external dependency (like pthreads).

The currently planned APIs include:

<?php

namespace phactor;

class FileHandle
{
    function read(int $length) : string;
    function write(string $data) : bool;
}

Example:

<?php

use phactor\{ActorSystem, Actor, ActorRef, FileHandle};

class Test extends Actor
{
    public function receive()
    {
        try {
            $fh = new FileHandle(__FILE__); // causes a context switch to open the file
        } catch (Throwable $t) {
            var_dump($t->getMessage());
        }
        ActorSystem::shutdown();
    }
}

$actorSystem = new ActorSystem();

new ActorRef(Test::class);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.