// -*- c++ -*-

/**
   A single runtime instance.

   The kernel is primarily responsible for managing the lifetime of
   schedulers, which in turn run rust tasks. It provides a memory
   allocator and logging service for use by other runtime components,
   it creates unique task and port ids and provides global access
   to ports by id.

   The kernel runs until there are no live schedulers.

   The kernel internally runs an additional, special scheduler called
   the 'osmain' (or platform) scheduler, which schedules tasks on the
   thread that is running the kernel (normally the thread on which the
   C main function was called). This scheduler may be used by Rust
   code for interacting with platform APIs that insist on being called
   from the main thread.

   The requirements of the osmain scheduler has resulted in a complex
   process for creating and running scheduler loops that involves
   a thing called a 'rust_sched_launcher_factory' whose function I've
   already forgotten. rust_scheduler is the main scheduler class,
   and tasks are scheduled on individual threads by rust_sched_loop.

   Ideally all the in-memory Rust state is encapsulated by a kernel
   instance, but there is still some truly global data in the runtime
   (like the check claims flag).
 */

#ifndef RUST_KERNEL_H
#define RUST_KERNEL_H

#include "rust_globals.h"

#include <map>
#include <vector>

#include "memory_region.h"
#include "rust_log.h"
#include "rust_sched_reaper.h"
#include "util/hash_map.h"

class rust_scheduler;
class rust_sched_driver;
class rust_sched_launcher_factory;
struct rust_task_thread;
class rust_port;

// Scheduler, task, and port handles. These uniquely identify within a
// single kernel instance the objects they represent.
typedef intptr_t rust_sched_id;
typedef intptr_t rust_task_id;
typedef intptr_t rust_port_id;

typedef std::map<rust_sched_id, rust_scheduler*> sched_map;

class rust_kernel {
    memory_region _region;
    rust_log _log;

    // The next task id
    rust_task_id max_task_id;

    // Protects max_port_id and port_table
    lock_and_signal port_lock;
    // The next port id
    rust_task_id max_port_id;
    hash_map<rust_port_id, rust_port *> port_table;

    lock_and_signal rval_lock;
    int rval;

    // Protects max_sched_id and sched_table, join_list
    lock_and_signal sched_lock;
    // The next scheduler id
    rust_sched_id max_sched_id;
    // A map from scheduler ids to schedulers. When this is empty
    // the kernel terminates
    sched_map sched_table;
    // A list of scheduler ids that are ready to exit
    std::vector<rust_sched_id> join_list;

    rust_sched_reaper sched_reaper;
    // The single-threaded scheduler that uses the main thread
    rust_sched_id osmain_scheduler;
    // Runs the single-threaded scheduler that executes tasks
    // on the main thread
    rust_sched_driver *osmain_driver;

    // An atomically updated count of the live, 'non-weak' tasks
    uintptr_t non_weak_tasks;
    // Protects weak_task_chans
    lock_and_signal weak_task_lock;
    // A list of weak tasks that need to be told when to exit
    std::vector<rust_port_id> weak_task_chans;

    rust_scheduler* get_scheduler_by_id_nolock(rust_sched_id id);
    void end_weak_tasks();

    // Used to communicate with the process-side, global libuv loop
    uintptr_t global_loop_chan;
    // Used to serialize access to getenv/setenv
    uintptr_t global_env_chan;

public:
    struct rust_env *env;

    rust_kernel(rust_env *env);

    void log(uint32_t level, char const *fmt, ...);
    void fatal(char const *fmt, ...);

    void *malloc(size_t size, const char *tag);
    void *calloc(size_t size, const char *tag);
    void *realloc(void *mem, size_t size);
    void free(void *mem);
    memory_region *region() { return &_region; }

    void fail();

    rust_sched_id create_scheduler(size_t num_threads);
    rust_sched_id create_scheduler(rust_sched_launcher_factory *launchfac,
                                   size_t num_threads, bool allow_exit);
    rust_scheduler* get_scheduler_by_id(rust_sched_id id);
    // Called by a scheduler to indicate that it is terminating
    void release_scheduler_id(rust_sched_id id);
    void wait_for_schedulers();
    int run();

#ifdef __WIN32__
    void win32_require(LPCTSTR fn, BOOL ok);
#endif

    rust_task_id generate_task_id();

    rust_port_id register_port(rust_port *port);
    rust_port *get_port_by_id(rust_port_id id);
    void release_port_id(rust_port_id tid);

    void set_exit_status(int code);

    rust_sched_id osmain_sched_id() { return osmain_scheduler; }

    void register_task();
    void unregister_task();
    void weaken_task(rust_port_id chan);
    void unweaken_task(rust_port_id chan);

    bool send_to_port(rust_port_id chan, void *sptr);

    uintptr_t* get_global_loop() { return &global_loop_chan; }
    uintptr_t* get_global_env_chan() { return &global_env_chan; }
};

template <typename T> struct kernel_owned {
    inline void *operator new(size_t size, rust_kernel *kernel,
                              const char *tag) {
        return kernel->malloc(size, tag);
    }

    void operator delete(void *ptr) {
        ((T *)ptr)->kernel->free(ptr);
    }
};

#endif /* RUST_KERNEL_H */

//
// Local Variables:
// mode: C++
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//
