/*
   +----------------------------------------------------------------------+
   | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) 2005 The PHP Group                                     |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.0 of the PHP 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.php.net/license/3_0.txt.                                  |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | license@php.net so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Author: Magnus Määttä <magnus@php.net>                               |
   +----------------------------------------------------------------------+
*/

/* $ Id: $ */

#include "php_realtime.h"
#include <sched.h>
#include <sys/mman.h>

#if HAVE_REALTIME

ZEND_DECLARE_MODULE_GLOBALS(realtime)


/* {{{ realtime_functions[] */
function_entry realtime_functions[] = {
    PHP_FE(realtime_mlockall, NULL)
    PHP_FE(realtime_munlockall, NULL)
    PHP_FE(realtime_sched_getscheduler, NULL)
    PHP_FE(realtime_sched_setscheduler, NULL)
    PHP_FE(realtime_sched_get_priority_max, NULL)
    PHP_FE(realtime_sched_get_priority_min, NULL)
    PHP_FE(realtime_sched_getaffinity, NULL)
    PHP_FE(realtime_sched_setaffinity, NULL)
    PHP_FE(realtime_sched_rr_get_interval, NULL)
    PHP_FE(realtime_sched_yield, NULL)
    PHP_FE(realtime_get_last_error, NULL)
    PHP_FE(realtime_strerror, NULL)
    { NULL, NULL, NULL }
};
/* }}} */


/* {{{ realtime_module_entry
 */
zend_module_entry realtime_module_entry = {
    STANDARD_MODULE_HEADER,
    "realtime",
    realtime_functions,
    PHP_MINIT(realtime),
    NULL,
    NULL,
    NULL,
    PHP_MINFO(realtime),
    "0.1",
    STANDARD_MODULE_PROPERTIES
};
/* }}} */

#ifdef COMPILE_DL_REALTIME
ZEND_GET_MODULE(realtime)
#endif

static void php_realtime_init_globals(zend_realtime_globals *realtime_globals TSRMLS_DC)
{
    realtime_globals->last_error = 0;
}

/* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(realtime)
{
    ZEND_INIT_MODULE_GLOBALS(realtime, php_realtime_init_globals, NULL);
    REGISTER_LONG_CONSTANT("REALTIME_MCL_CURRENT",    MCL_CURRENT, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("REALTIME_MCL_FUTURE",    MCL_FUTURE, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("REALTIME_SCHED_OTHER",    SCHED_OTHER, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("REALTIME_SCHED_RR",        SCHED_RR, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("REALTIME_SCHED_FIFO",    SCHED_FIFO, CONST_CS | CONST_PERSISTENT);
    return SUCCESS;
}
/* }}} */


/* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(realtime)
{
    php_info_print_table_start();
    php_info_print_table_row(2, "Revision", "$Revision: 1.1 $");
    php_info_print_table_end();
}
/* }}} */


/* {{{ bool realtime_mlockall(int flags)
   Lock all pages mapped into the address space of the calling process. */
PHP_FUNCTION(realtime_mlockall)
{
    long flags;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &flags)  == FAILURE) {
        WRONG_PARAM_COUNT;
    }

    if (!mlockall(flags)) {
        RETURN_TRUE;
    } else {
        REALTIME_G(last_error) = errno;
        RETURN_FALSE;
    }
}
/* }}} */

/* {{{ bool realtime_munlockall()
   Unlock all pages mapped into the address space of the calling process. */
PHP_FUNCTION(realtime_munlockall)
{
    if (ZEND_NUM_ARGS() TSRMLS_CC != 0) {
        WRONG_PARAM_COUNT;
    }

    if (!munlockall()) {
        RETURN_TRUE;
    } else {
        REALTIME_G(last_error) = errno;
        RETURN_FALSE;
    }
}
/* }}} */

/* {{{ proto int realtime_sched_setscheduler([int pid])
   Get scheduling algorithm */
PHP_FUNCTION(realtime_sched_getscheduler)
{
    int retval;
    long pid = 0;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &pid)  == FAILURE) {
        WRONG_PARAM_COUNT;
    }

    retval = sched_getscheduler(pid);
    if (retval >= 0) {
        RETURN_LONG(retval);
    } else {
        REALTIME_G(last_error) = errno;
        RETURN_FALSE;
    }
}
/* }}} */

/* {{{ proto bool realtime_sched_setscheduler(int policy, int priority [, int pid])
   Set scheduling algorithm/parameters */
PHP_FUNCTION(realtime_sched_setscheduler)
{
    long pid = 0;
    long policy, priority;
    struct sched_param php_param;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll|l", &policy, &priority, &pid)  == FAILURE) {
        WRONG_PARAM_COUNT;
    }

    php_param.sched_priority = priority;

    if (!sched_setscheduler(pid, policy, &php_param)) {
        RETURN_TRUE;
    } else {
        REALTIME_G(last_error) = errno;
        RETURN_FALSE;
    }
}
/* }}} */

/* {{{ proto int realtime_sched_get_priority_max(int policy)
   Get the maximum priority value that can be used with the scheduling
   algorithm identified by policy. */
PHP_FUNCTION(realtime_sched_get_priority_max)
{
    int retval = 0;
    long sched;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &sched)  == FAILURE) {
        WRONG_PARAM_COUNT;
    }

    retval = sched_get_priority_max(sched);
    if (retval < 0) {
        REALTIME_G(last_error) = errno;
    }

    RETURN_LONG(retval);
}
/* }}} */

/* {{{ proto int realtime_sched_setscheduler(int policy)
   Get the minimum priority value that can be used with the scheduling
   algorithm identified by policy. */
PHP_FUNCTION(realtime_sched_get_priority_min)
{
    int retval = 0;
    long policy;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &policy)  == FAILURE) {
        WRONG_PARAM_COUNT;
    }

    retval = sched_get_priority_max(policy);
    if (retval < 0) {
        REALTIME_G(last_error) = errno;
        RETURN_FALSE;
    }

    RETURN_LONG(retval);
}
/* }}} */

/* {{{ proto int realtime_sched_getaffinity(int pid)
   Get a process's CPU affinity mask. */
PHP_FUNCTION(realtime_sched_getaffinity)
{
    long pid = 0;
    long mask = 0;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &pid) == FAILURE) {
        WRONG_PARAM_COUNT;
    }

    if (!sched_getaffinity(0, sizeof(mask), &mask)) {
        RETURN_LONG(mask);
    } else {
        REALTIME_G(last_error) = errno;
        RETURN_FALSE;
    }
}
/* }}} */

/* {{{ proto int realtime_sched_getaffinity(array cpus [,int pid])
   Set a process's CPU affinity mask. */
PHP_FUNCTION(realtime_sched_setaffinity)
{
    zval **tmp;
    HashPosition pos;
    HashTable *array_hash;
    long pid, mask = 0;
    zval *array;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &pid)  == FAILURE) {
        WRONG_PARAM_COUNT;
    }

    array_hash = Z_ARRVAL_P(array);
    for (zend_hash_internal_pointer_reset_ex(array_hash, &pos);
            zend_hash_get_current_data_ex(array_hash, (void **) &tmp, &pos) == SUCCESS;
            zend_hash_move_forward_ex(array_hash, &pos)) {
        if (Z_TYPE_PP(tmp) == IS_LONG) {
            mask |= (long) pow(2, Z_LVAL_PP(tmp));
        }
    }

    if (!sched_setaffinity(pid, sizeof(mask), &mask)) {
        RETURN_TRUE;
    } else {
        REALTIME_G(last_error) = errno;
        RETURN_FALSE;
    }
}
/* }}} */

/* {{{ proto array realtime_sched_rr_get_interval([int pid])
   Get the SCHED_RR interval for the named process */
PHP_FUNCTION(realtime_sched_rr_get_interval)
{
    long pid = 0;
    struct timespec php_rr_interval;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &pid)  == FAILURE) {
        WRONG_PARAM_COUNT;
    }

    if (!sched_rr_get_interval(pid, &php_rr_interval)) {
        array_init(return_value);
        add_assoc_long_ex(return_value, "seconds", 8, php_rr_interval.tv_sec);
        add_assoc_long_ex(return_value, "nanoseconds", 12, php_rr_interval.tv_nsec);
        return;
    } else {
        REALTIME_G(last_error) = errno;
        RETURN_FALSE;
    }
}
/* }}} */

/* {{{ proto int realtime_sched_yield(void)
   Move the process to the end of the work queue. */
PHP_FUNCTION(realtime_sched_yield)
{
    if (ZEND_NUM_ARGS() TSRMLS_CC != 0) {
        WRONG_PARAM_COUNT;
    }

    if (!sched_yield()) {
        RETURN_TRUE;
    } else {
        REALTIME_G(last_error) = errno;
        RETURN_FALSE;
    }
}
/* }}} */

/* {{{ proto int realtime_get_last_error(void)
   Retrieve the error number set by the last realtime function which failed. */
PHP_FUNCTION(realtime_get_last_error)
{
    if (ZEND_NUM_ARGS() TSRMLS_CC != 0) {
        WRONG_PARAM_COUNT;
    }

    RETURN_LONG(REALTIME_G(last_error));
}
/* }}} */

/* {{{ proto int realtime_strerror(int errno)
   Retrieve the system error message associated with the given errno. */
PHP_FUNCTION(realtime_strerror)
{
    long error;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &error) == FAILURE) {
        WRONG_PARAM_COUNT;
    }

    RETURN_STRING(strerror(error), 1);
}


#endif /* HAVE_REALTIME */

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: noet sw=4 ts=4 fdm=marker
 * vim<600: noet sw=4 ts=4
 */