HEX
Server: Apache/2.4.59 (Debian)
System: Linux keymana 4.19.0-21-cloud-amd64 #1 SMP Debian 4.19.249-2 (2022-06-30) x86_64
User: lijunjie (1003)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //home/lijunjie/swoole-cli/swoole-src-4.8.13/src/server/task_worker.cc
/*
  +----------------------------------------------------------------------+
  | Swoole                                                               |
  +----------------------------------------------------------------------+
  | This source file is subject to version 2.0 of the Apache license,    |
  | that is bundled with this package in the file LICENSE, and is        |
  | available through the world-wide-web at the following url:           |
  | http://www.apache.org/licenses/LICENSE-2.0.html                      |
  | If you did not receive a copy of the Apache2.0 license and are unable|
  | to obtain it through the world-wide-web, please send a note to       |
  | license@swoole.com so we can mail you a copy immediately.            |
  +----------------------------------------------------------------------+
  | Author: Tianfeng Han  <rango@swoole.com>                             |
  +----------------------------------------------------------------------+
*/

#include "swoole_server.h"

namespace swoole {
using network::Socket;

static void TaskWorker_signal_init(ProcessPool *pool);
static int TaskWorker_onPipeReceive(Reactor *reactor, Event *event);
static int TaskWorker_loop_async(ProcessPool *pool, Worker *worker);
static void TaskWorker_onStart(ProcessPool *pool, int worker_id);
static void TaskWorker_onStop(ProcessPool *pool, int worker_id);
static int TaskWorker_onTask(ProcessPool *pool, EventData *task);

/**
 * after pool->create, before pool->start
 */
void Server::init_task_workers() {
    ProcessPool *pool = &gs->task_workers;
    pool->ptr = this;
    pool->onTask = TaskWorker_onTask;
    pool->onWorkerStart = TaskWorker_onStart;
    pool->onWorkerStop = TaskWorker_onStop;
    /**
     * Make the task worker support asynchronous
     */
    if (task_enable_coroutine) {
        if (task_ipc_mode == TASK_IPC_MSGQUEUE || task_ipc_mode == TASK_IPC_PREEMPTIVE) {
            swoole_error("cannot use msgqueue when task_enable_coroutine is enable");
            return;
        }
        pool->main_loop = TaskWorker_loop_async;
    }
    if (task_ipc_mode == TASK_IPC_PREEMPTIVE) {
        pool->schedule_by_sysvmsg = true;
    }
}

static int TaskWorker_call_command_handler(ProcessPool *pool, EventData *req) {
    Server *serv = (Server *) pool->ptr;
    int command_id = req->info.server_fd;
    auto iter = serv->command_handlers.find(command_id);
    if (iter == serv->command_handlers.end()) {
        swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_INVALID_COMMAND, "Unknown command[%d]", command_id);
        return SW_OK;
    }

    Server::Command::Handler handler = iter->second;
    PacketPtr packet;
    if (!Server::task_unpack(req, sw_tg_buffer(), &packet)) {
        return SW_OK;
    }

    auto result = handler(serv, std::string(packet.data, packet.length));

    SendData task{};
    task.info.fd = req->info.fd;
    task.info.reactor_id = SwooleWG.worker->id;
    task.info.server_fd = -1;
    task.info.type = SW_SERVER_EVENT_COMMAND_RESPONSE;
    task.info.len = result.length();
    task.data = result.c_str();

    return serv->message_bus.write(serv->get_command_reply_socket(), &task) ? SW_OK : SW_ERR;
}

static int TaskWorker_onTask(ProcessPool *pool, EventData *task) {
    int ret = SW_OK;
    Server *serv = (Server *) pool->ptr;
    serv->last_task = task;

    if (task->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) {
        serv->onPipeMessage(serv, task);
    } else if (task->info.type == SW_SERVER_EVENT_COMMAND_REQUEST) {
        ret = TaskWorker_call_command_handler(pool, task);
    } else {
        ret = serv->onTask(serv, task);
    }

    return ret;
}

bool Server::task_pack(EventData *task, const void *_data, size_t _length) {
    task->info.type = SW_SERVER_EVENT_TASK;
    task->info.fd = SwooleG.current_task_id++;
    task->info.reactor_id = SwooleG.process_id;
    task->info.time = swoole::microtime();

    if (_length < SW_IPC_MAX_SIZE - sizeof(task->info)) {
        memcpy(task->data, _data, _length);
        task->info.len = _length;
        return true;
    }

    PacketTask pkg{};
    File file = make_tmpfile();
    if (!file.ready()) {
        return false;
    }

    if (file.write_all(_data, _length) != _length) {
        swoole_warning("write to tmpfile failed");
        return false;
    }

    task->info.len = sizeof(pkg);
    task->info.ext_flags |= SW_TASK_TMPFILE;
    swoole_strlcpy(pkg.tmpfile, file.get_path().c_str(), sizeof(pkg.tmpfile));
    pkg.length = _length;
    memcpy(task->data, &pkg, sizeof(pkg));

    return true;
}

bool Server::task_unpack(EventData *task, String *buffer, PacketPtr *packet) {
    if (!(task->info.ext_flags & SW_TASK_TMPFILE)) {
        packet->data = task->data;
        packet->length = task->info.len;
        return true;
    }

    PacketTask _pkg{};
    memcpy(&_pkg, task->data, sizeof(_pkg));

    File fp(_pkg.tmpfile, O_RDONLY);
    if (!fp.ready()) {
        swoole_sys_warning("open(%s) failed", _pkg.tmpfile);
        return false;
    }
    if (buffer->size < _pkg.length && !buffer->extend(_pkg.length)) {
        return false;
    }
    if (fp.read_all(buffer->str, _pkg.length) != _pkg.length) {
        return false;
    }
    if (!(task->info.ext_flags & SW_TASK_PEEK)) {
        unlink(_pkg.tmpfile);
    }
    buffer->length = _pkg.length;
    packet->data = buffer->str;
    packet->length = buffer->length;
    return true;
}

static void TaskWorker_signal_init(ProcessPool *pool) {
    /**
     * use user settings
     */
    SwooleG.use_signalfd = SwooleG.enable_signalfd;

    swoole_signal_set(SIGHUP, nullptr);
    swoole_signal_set(SIGPIPE, nullptr);
    swoole_signal_set(SIGUSR1, Server::worker_signal_handler);
    swoole_signal_set(SIGUSR2, nullptr);
    swoole_signal_set(SIGTERM, Server::worker_signal_handler);
#ifdef SIGRTMIN
    swoole_signal_set(SIGRTMIN, Server::worker_signal_handler);
#endif
}

static void TaskWorker_onStart(ProcessPool *pool, int worker_id) {
    Server *serv = (Server *) pool->ptr;
    SwooleG.process_id = worker_id;

    /**
     * Make the task worker support asynchronous
     */
    if (serv->task_enable_coroutine) {
        if (swoole_event_init(0) < 0) {
            swoole_error("[TaskWorker] create reactor failed");
            return;
        }
        SwooleG.enable_signalfd = 1;
    } else {
        SwooleG.enable_signalfd = 0;
        SwooleTG.reactor = nullptr;
    }

    TaskWorker_signal_init(pool);
    serv->worker_start_callback();

    Worker *worker = pool->get_worker(worker_id);
    worker->start_time = ::time(nullptr);
    worker->request_count = 0;
    SwooleWG.worker = worker;
    SwooleWG.worker->status = SW_WORKER_IDLE;
    /**
     * task_max_request
     */
    if (pool->max_request > 0) {
        SwooleWG.run_always = false;
        SwooleWG.max_request = pool->get_max_request();
    } else {
        SwooleWG.run_always = true;
    }
}

static void TaskWorker_onStop(ProcessPool *pool, int worker_id) {
    swoole_event_free();
    Server *serv = (Server *) pool->ptr;
    serv->worker_stop_callback();
}

/**
 * receive data from worker process
 */
static int TaskWorker_onPipeReceive(Reactor *reactor, Event *event) {
    EventData task;
    ProcessPool *pool = (ProcessPool *) reactor->ptr;
    Worker *worker = SwooleWG.worker;
    Server *serv = (Server *) pool->ptr;

    if (event->socket->read(&task, sizeof(task)) > 0) {
        worker->status = SW_WORKER_BUSY;
        int retval = TaskWorker_onTask(pool, &task);
        worker->status = SW_WORKER_IDLE;
        worker->request_count++;
        // maximum number of requests, process will exit.
        if (!SwooleWG.run_always && worker->request_count >= SwooleWG.max_request) {
            serv->stop_async_worker(worker);
        }
        return retval;
    } else {
        swoole_sys_warning("read(%d, %ld) failed", event->fd, sizeof(task));
        return SW_ERR;
    }
}

/**
 * async task worker
 */
static int TaskWorker_loop_async(ProcessPool *pool, Worker *worker) {
    Server *serv = (Server *) pool->ptr;
    Socket *socket = worker->pipe_worker;
    worker->status = SW_WORKER_IDLE;

    socket->set_nonblock();
    sw_reactor()->ptr = pool;
    swoole_event_add(socket, SW_EVENT_READ);
    swoole_event_set_handler(SW_FD_PIPE, TaskWorker_onPipeReceive);

    for (uint i = 0; i < serv->worker_num + serv->task_worker_num; i++) {
        worker = serv->get_worker(i);
        worker->pipe_master->buffer_size = UINT_MAX;
        worker->pipe_worker->buffer_size = UINT_MAX;
    }

    return swoole_event_wait();
}

/**
 * Send the task result to worker
 */
int Server::reply_task_result(const char *data, size_t data_len, int flags, EventData *current_task) {
    EventData buf;
    sw_memset_zero(&buf.info, sizeof(buf.info));
    if (task_worker_num < 1) {
        swoole_warning("cannot use Server::task()/Server::finish() method, because no set [task_worker_num]");
        return SW_ERR;
    }
    if (current_task == nullptr) {
        current_task = last_task;
    }
    if (current_task->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) {
        swoole_warning("Server::task()/Server::finish() is not supported in onPipeMessage callback");
        return SW_ERR;
    }
    if (current_task->info.ext_flags & SW_TASK_NOREPLY) {
        swoole_warning("Server::finish() can only be used in the worker process");
        return SW_ERR;
    }

    uint16_t source_worker_id = current_task->info.reactor_id;
    Worker *worker = get_worker(source_worker_id);

    if (worker == nullptr) {
        swoole_warning("invalid worker_id[%d]", source_worker_id);
        return SW_ERR;
    }

    int ret;
    // for swoole_server_task
    if (current_task->info.ext_flags & SW_TASK_NONBLOCK) {
        // write to file
        if (!task_pack(&buf, data, data_len)) {
            swoole_warning("large task pack failed()");
            return SW_ERR;
        }
        // callback function
        if (current_task->info.ext_flags & SW_TASK_CALLBACK) {
            flags |= SW_TASK_CALLBACK;
        } else if (current_task->info.ext_flags & SW_TASK_COROUTINE) {
            flags |= SW_TASK_COROUTINE;
        }
        buf.info.ext_flags |= flags;
        buf.info.type = SW_SERVER_EVENT_FINISH;
        buf.info.fd = current_task->info.fd;

        if (worker->pool->use_socket && worker->pool->stream_info_->last_connection) {
            uint32_t _len = htonl(data_len);
            ret = worker->pool->stream_info_->last_connection->send_blocking((void *) &_len, sizeof(_len));
            if (ret > 0) {
                ret = worker->pool->stream_info_->last_connection->send_blocking(data, data_len);
            }
        } else {
            ret = send_to_worker_from_worker(worker, &buf, sizeof(buf.info) + buf.info.len, SW_PIPE_MASTER);
        }
    } else {
        uint64_t flag = 1;

        /**
         * Use worker shm store the result
         */
        EventData *result = &(task_result[source_worker_id]);
        Pipe *pipe = task_notify_pipes.at(source_worker_id).get();

        // lock worker
        worker->lock->lock();

        if (current_task->info.ext_flags & SW_TASK_WAITALL) {
            sw_atomic_t *finish_count = (sw_atomic_t *) result->data;
            char *_tmpfile = result->data + 4;
            File file(_tmpfile, O_APPEND | O_WRONLY);
            if (file.ready()) {
                if (!task_pack(&buf, data, data_len)) {
                    swoole_warning("large task pack failed()");
                    buf.info.len = 0;
                }
                buf.info.ext_flags |= flags;
                buf.info.type = SW_SERVER_EVENT_FINISH;
                buf.info.fd = current_task->info.fd;
                size_t bytes = sizeof(buf.info) + buf.info.len;
                if (file.write_all(&buf, bytes) != bytes) {
                    swoole_sys_warning("write(%s, %ld) failed", _tmpfile, bytes);
                }
                sw_atomic_fetch_add(finish_count, 1);
            }
        } else {
            if (!task_pack(result, data, data_len)) {
                // unlock worker
                worker->lock->unlock();
                swoole_warning("large task pack failed()");
                return SW_ERR;
            }
            result->info.ext_flags |= flags;
            result->info.type = SW_SERVER_EVENT_FINISH;
            result->info.fd = current_task->info.fd;
        }

        // unlock worker
        worker->lock->unlock();

        while (1) {
            ret = pipe->write(&flag, sizeof(flag));
            auto _sock = pipe->get_socket(true);
            if (ret < 0 && _sock->catch_write_error(errno) == SW_WAIT) {
                if (_sock->wait_event(-1, SW_EVENT_WRITE) == 0) {
                    continue;
                }
            }
            break;
        }
    }
    if (ret < 0) {
        if (swoole_get_last_error() == EAGAIN || swoole_get_last_error() == SW_ERROR_SOCKET_POLL_TIMEOUT) {
            swoole_warning("send result to worker timed out");
        } else {
            swoole_sys_warning("send result to worker failed");
        }
    }
    return ret;
}
}  // namespace swoole