/*******************************************************************************

    D binding for Linux specific scheduler control and thread spawning
    methods.

    Defines functions sched_setaffinity and sched_getaffinity and the data
    types they operate on, as well as clone and unshare and their related
    constants.

    Copyright:  Copyright (c) 2016 Sociomantic Labs. All rights reserved.
    License:    $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
    Authors:    Nemanja Boric

*******************************************************************************/


module core.sys.linux.sched;

import core.bitop : popcnt;
import core.stdc.stdlib : malloc, free;
import core.sys.posix.sched;
import core.sys.posix.config;
import core.sys.posix.sys.types;

version (linux):
extern (C):
@nogc:
nothrow:


private // helpers
{

    /* Size definition for CPU sets.  */
    enum
    {
        __CPU_SETSIZE = 1024,
        __NCPUBITS  = 8 * cpu_mask.sizeof,
    }

    /* Macros */

    /* Basic access functions.  */
    size_t __CPUELT()(size_t cpu) pure
    {
        return cpu / __NCPUBITS;
    }
    cpu_mask __CPUMASK()(size_t cpu) pure
    {
        return 1UL << (cpu % __NCPUBITS);
    }

    cpu_set_t* __CPU_ALLOC()(size_t count)
    {
        return cast(cpu_set_t*) malloc(__CPU_ALLOC_SIZE(count));
    }

    size_t __CPU_ALLOC_SIZE()(size_t count) pure
    {
        return ((count + __NCPUBITS - 1) / __NCPUBITS) * cpu_mask.sizeof;
    }

    void __CPU_FREE()(cpu_set_t* set)
    {
        free(cast(void*) set);
    }

    cpu_mask __CPU_SET_S()(size_t cpu, size_t setsize, cpu_set_t* cpusetp) pure
    {
        if (cpu < 8 * setsize)
        {
            cpusetp.__bits[__CPUELT(cpu)] |= __CPUMASK(cpu);
            return __CPUMASK(cpu);
        }

        return 0;
    }

    bool __CPU_ISSET_S()(size_t cpu, size_t setsize, cpu_set_t* cpusetp) pure
    {
        if (cpu < 8 * setsize)
            return (cpusetp.__bits[__CPUELT(cpu)] & __CPUMASK(cpu)) != 0;
        return false;
    }

    int __CPU_COUNT_S()(size_t setsize, cpu_set_t* cpusetp) pure
    {
        int s = 0;
        foreach (i; cpusetp.__bits[0 .. (setsize / cpu_mask.sizeof)])
            s += popcnt(i);
        return s;
    }
}

/// Type for array elements in 'cpu_set_t'.
alias cpu_mask = c_ulong;

/// Data structure to describe CPU mask.
struct cpu_set_t
{
    cpu_mask[__CPU_SETSIZE / __NCPUBITS] __bits;
}

/// Access macros for 'cpu_set' (missing a lot of them)

cpu_set_t* CPU_ALLOC()(size_t count)
{
    return __CPU_ALLOC(count);
}

size_t CPU_ALLOC_SIZE()(size_t count) pure
{
    return __CPU_ALLOC_SIZE(count);
}

void CPU_FREE()(cpu_set_t* set)
{
    __CPU_FREE(set);
}

cpu_mask CPU_SET()(size_t cpu, cpu_set_t* cpusetp) pure
{
     return __CPU_SET_S(cpu, cpu_set_t.sizeof, cpusetp);
}

bool CPU_ISSET()(size_t cpu, cpu_set_t* cpusetp) pure
{
    return __CPU_ISSET_S(cpu, cpu_set_t.sizeof, cpusetp);
}

int CPU_COUNT()(cpu_set_t* cpusetp) pure
{
    return __CPU_COUNT_S(cpu_set_t.sizeof, cpusetp);
}

int CPU_COUNT_S()(size_t setsize, cpu_set_t* cpusetp) pure
{
    return __CPU_COUNT_S(setsize, cpusetp);
}

/* Scheduler control functions */
int sched_setaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);
int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);

/* Clone and related functions and constants */
int clone(int function(void*), void* child_stack, int flags, void* arg, ...);
int unshare(int flags) @trusted;

version (CRuntime_Glibc)
{
    /* Determine CPU on which the calling thread is running */
    int sched_getcpu();
}
else version (CRuntime_Musl)
{
    int sched_getcpu();
}

/* Reassociate the calling thread with namespace referred to by fd */
int setns(int fd, int nstype);

enum CLONE_FILES = 0x400;
enum CLONE_FS = 0x200;
enum CLONE_NEWCGROUP = 0x2000000;
enum CLONE_NEWIPC = 0x8000000;
enum CLONE_NEWNET = 0x40000000;
enum CLONE_NEWNS = 0x20000;
enum CLONE_NEWPID = 0x20000000;
enum CLONE_NEWUSER = 0x10000000;
enum CLONE_NEWUTS = 0x4000000;
enum CLONE_SIGHAND = 0x800;
enum CLONE_SYSVSEM = 0x40000;
enum CLONE_THREAD = 0x10000;
enum CLONE_VFORK = 0x4000;
enum CLONE_VM = 0x100;
