• ### What is your GameDev Story?

This topic is 3832 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

##### Share on other sites
The basic point of a task system is to allow time-sharing between processes without incurring the overhead of normal multi-threading and without having to solve the same synchronization and atomicity issues. At its most elementary level, a task is a sequence of functions to be called one after another to perform a process, the advantage (when compared to performing the process with a single function call) being that you can 'pause' the execution of the task in-between function calls in order to do other things, and the disadvantage being that cutting up a process into independent functions is hard. As for a kernel, it is nothing more than a set of tasks with a decision algorithm that selects a task and calls its next function.

Using this definition, it appears that a task has no reason to know anything about the kernel, and there is therefore no need for a kernel singleton or a pointer from task to kernel. So, for the basic layout, I would consider:

interface ITask {  public bool Done { get; }  public void Step();}class Kernel{  public List<ITask> Tasks;  public void Run() { /* Select a task and run it. */ }}

In this approach, your tasks (and subsystems) become servers and function calls become messages. I would suggest a typical architecture for servers: propose a series of messages that can be sent to that server (both synchronous and asynchronous) and group them in an interface. Then, have the server implement both that server and the ITask interface. For example:

interface IScore{  // Asynchronous functions  public void AddToScore(int);  public Result<int> GetScore();  public void Shutdown();}class ScoreBar : IScore, ITask{  int         score;  List<int>   additions;  Result<int> next;  bool        alive;  public AddToScore(int x)   {     this.additions.Add(x);   }  public Result<int> GetScore()  {    if (this.next == null)      this.next = new Result<int>();        return this.next;  }  public void Shutdown()  {    this.alive = false;  }  public void Step()  {      foreach (int add in this.additions)      this.score += add;    this.additions.Clear();        if (this.next != null)      this.next.SetValue(this.score);    this.next = null;  }    public bool Done   {    get { return this.next == null && !this.alive; }  }}

Of course, a score object is a very bad idea for a subsystem (because it will have to be updated anyway, and as such fits better as a synchronous object). But you get the idea. However, of particular interest in this architecture is the ability to dispatch the received messages to another computer or thread, as the asynchronous nature of the system allows for it.

As for the actual connection between the servers (that is, the ability to connect the server which provides Foo to the server which needs Foo), this should ideally be done by the creator of these servers. That is, when you create your Foo-user, you have a reference to the Foo-provider that you give to the Foo-user, thereby granting him the ability to connect to the Foo-provider asynchronously. For instance:

interface IPlayerServer{  public void HasKilledEnemy(int score);}class PlayerServer : IPlayerServer{  IScore scoreServer;  public IScore ScoreServer   {     get { assert (this.scoreServer); return this.scoreServer; }    set { this.scoreServer = value; }  }    void HasKilledEnemy(int score)  {    this.ScoreServer.AddToScore(score);  }}// Somewhere in the subsystem creation functionIScore score = new Score();PlayerServer player = new PlayerServer();player.ScoreServer = score;

##### Share on other sites
Quote:
 but leads to annoying complexity when two-way communication becomes involved (for instance, querying an object for data in an asynchronous world becomes a 'send message, return, receive message or return, process' instead of a plain old function call). It also creates issue with sequential dependencies (such as the world update function having to wait for the render function to be done rendering) but you can design your kernel so that it synchronizes the tasks based on the current frame.

If you have a global message queue rather than follow an actor model (each subsystem with its own queue) then i think its possible to do this using coroutines/continuations and futures to abstract the round trip messaging. this allows state querying without the slow context switching with reasonable syntactic clarity. it might be possible to flag the sends in priority to avoid the messages getting clogged up at the back of the global queue. ive been a bit stumped on this so i just throw out the idea although i havent implemented.

##### Share on other sites
Quote:
 Original post by chairthrowerIf you have a global message queue rather than follow an actor model (each subsystem with its own queue) then i think its possible to do this using coroutines/continuations and futures to abstract the round trip messaging. this allows state querying without the slow context switching with reasonable syntactic clarity. it might be possible to flag the sends in priority to avoid the messages getting clogged up at the back of the global queue. ive been a bit stumped on this so i just throw out the idea although i havent implemented.

Yes, this would work. However, it's mostly an attempt to patch what is already known to be a design issue: synchronous communication in a server-based architecture is subject to lag. This is one of the main reasons why I don't use a server-based model when I work, and instead use an event-based model.

With events, it becomes elementary to use continuations to solve the asynchronous issue (and it also solves the issue of synchronizing with time-based ticks. The main issue with event-based programming is that it somewhat reduces the isolation of the systems (since now a task can fork as many sub-tasks as necessary to handle asynchronous communication, and therefore a subsystem would be a set of tasks instead of a single one), though a small code layout effort usually helps reducing this.

##### Share on other sites
Thank you, now I understand..

• ### What is your GameDev Story?

In 2019 we are celebrating 20 years of GameDev.net! Share your GameDev Story with us.

• 9
• 33
• 16
• 11
• 10
• ### Forum Statistics

• Total Topics
634123
• Total Posts
3015633
×

## Important Information

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!