↑ Back to main site.

EPOS

Experimental Protected-mode Operating System

ipc_thoth.c
/*
EPOS
http://www.atanaslaskov.com/epos/

FILE: ipc_thoth.c
DESCRIPTION:
Inter-Process Communication IPC in the style of Thoth OS
 
BSD LICENSE
Copyright (c) 2006, Atanas Laskov
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
   1. Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
   2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ATANAS LASKOV BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


// Headers of the kernel
#include "..\..\memory\memory.h"
#include "..\..\process\process.h"
#include "..\..\gdt\gdt.h"
#include "..\process\process.h"
#include "..\..\paging\paging.h"

// thoth header
#include "ipc_thoth.h"

static struct ThothInbox g_arThInbox[MAX_PROCESS_CNT];

//
// Initialization
//
void k_thothInit()
{
    PID i;

    for(i=0; i<MAX_PROCESS_CNT; i++)
    {
        g_arThInbox[i].nMsgCnt = 0;
        g_arThInbox[i].bFlags  = 0;
    }
}


//
// Kernel Call - Send a message
//
void thothSend(struct ThothMsg *pMsg)
{
    PID dest_pid    = pMsg->pid;
    PID current_pid = tmGetCrProcessID();

    // Check if the target process exists
    if( (dest_pid >= MAX_PROCESS_CNT) || !(tmGetProcessState(dest_pid)&T_STATE__ALIVE) )
    {
        pMsg->pid = PID_INVALID;
        return;
    }

    //Check if the target inbox has free space
    if( g_arThInbox[dest_pid].nMsgCnt >= THOTH_INBOXSZ)
    {
        //Mark the reason for blocking
        g_arThInbox[current_pid].bFlags |= THOTH_IF_BLOCKED;
        g_arThInbox[current_pid].bFlags |= THOTH_IF_FULL;

        //Temporary storgae of the message causing the block
        g_arThInbox[current_pid].pMsgOverflow = pMsg;
        g_arThInbox[current_pid].pidOverflow  = dest_pid;

        //Temporary suspend the process
        tmSleep(current_pid);
        tmAddCharge();
        return;
    }

    //Check if the target process is blocked because of empty inbox
    if(g_arThInbox[dest_pid].bFlags & THOTH_IF_EMPTY)
    {
        struct ThothMsg *pOverflowMsg = g_arThInbox[dest_pid].pMsgOverflow;

        //Copy received message in the memory space of the blocked process it is intended for
        memcpy(pOverflowMsg, pMsg, sizeof(struct ThothMsg));
        pOverflowMsg->pid = current_pid;

        //Unblock the recipient
        g_arThInbox[dest_pid].bFlags = 0;
        tmWakeUp(dest_pid);
       
        //Mark the reason for blocking the current process
        g_arThInbox[current_pid].bFlags |= THOTH_IF_BLOCKED;
        g_arThInbox[current_pid].bFlags |= THOTH_IF_REPLY;
        g_arThInbox[current_pid].pidOverflow  = dest_pid;
        g_arThInbox[current_pid].pMsgOverflow = pMsg;

        //Suspend the current process until it a Reply
        tmSleep(current_pid);
        tmAddCharge();
        return;
    }

    //Copy the message form the argument to the target inbox
    memcpy(g_arThInbox[dest_pid].arMsg + g_arThInbox[dest_pid].nMsgCnt,
        pMsg, sizeof(struct ThothMsg));
    g_arThInbox[dest_pid].arMsg[ g_arThInbox[dest_pid].nMsgCnt++ ].pid = current_pid;

    pMsg->pid = dest_pid;

    //Mark the reason for blocking
    g_arThInbox[current_pid].bFlags |= THOTH_IF_BLOCKED;
    g_arThInbox[current_pid].bFlags |= THOTH_IF_REPLY;
    g_arThInbox[current_pid].pidOverflow  = dest_pid;
    g_arThInbox[current_pid].pMsgOverflow = pMsg;

    //Suspend the process until a Reply
    tmSleep(current_pid);
    tmAddCharge();
}


//
// Kernel Call - Receive a message
//
void thothReceive(struct ThothMsg *pMsg)
{
    PID src_pid     = pMsg->pid;
    PID current_pid = tmGetCrProcessID();
    PID nPid;
    unsigned bReceiveAny=0;
    unsigned i;
    unsigned nMsg;

    //If the process-source of the message has PID_INVALID, enable receive-any
    if( src_pid==PID_INVALID)
    {
        bReceiveAny = 1;
    }
    //Otherwise enable receive-specific and check validity of the target
    else if( (src_pid >= MAX_PROCESS_CNT) || !(tmGetProcessState(src_pid)&T_STATE__ALIVE) )
    {
        pMsg->pid = PID_INVALID;
        return;
    }

    //Receive-any
    if(bReceiveAny)
    {
        //Check if the target inbox is empty
        if( g_arThInbox[current_pid].nMsgCnt == 0)
        {
            //Copy the argument - the kernel call will be served
            //when there is a message in the inbox
            g_arThInbox[current_pid].pMsgOverflow = pMsg;
            g_arThInbox[current_pid].pidOverflow  = current_pid; //fix!

            //Mark the reason for blcoking
            g_arThInbox[current_pid].bFlags |= THOTH_IF_BLOCKED;
            g_arThInbox[current_pid].bFlags |= THOTH_IF_EMPTY;

            //Temporarily suspend the process
            tmSleep(current_pid);
            tmAddCharge();
            return;
        }

        //Extract the message
        memcpy(pMsg, g_arThInbox[current_pid].arMsg, sizeof(struct ThothMsg));
        //pMsg->pid = g_arThInbox[current_pid].arMsg[0].pid;

        //Shift remaining messages
        for(i=1; i<g_arThInbox[current_pid].nMsgCnt; i++){
            memcpy(g_arThInbox[current_pid].arMsg+i-1,
                g_arThInbox[current_pid].arMsg+i, sizeof(struct ThothMsg));
        }
        g_arThInbox[current_pid].nMsgCnt--;
    }
    //Receive-specific
    else
    {
        //Find the oldest message sent from the process
        //with id for src_pid
        nMsg = THOTH_INBOXSZ;
        for(i=0; i<g_arThInbox[current_pid].nMsgCnt; i++){
            if(g_arThInbox[current_pid].arMsg[i].pid == src_pid)
            {
                nMsg = i;
                break;
            }
        }

        //Check if there is a sent from src_pid
        if(nMsg>=THOTH_INBOXSZ)
        {
            //Store the address of the argument - the kernel call
            //will be served when a message appears in the inbox
            g_arThInbox[current_pid].pMsgOverflow = pMsg;
            g_arThInbox[current_pid].pidOverflow = src_pid;

            //Mark the reason for blocking
            g_arThInbox[current_pid].bFlags |= THOTH_IF_BLOCKED;
            g_arThInbox[current_pid].bFlags |= THOTH_IF_EMPTY;

            //Temporarily suspend the process
            tmSleep(current_pid);
            tmAddCharge();
            return;
        }

        //Extract the message
        memcpy(pMsg, g_arThInbox[current_pid].arMsg+nMsg, sizeof(struct ThothMsg));
        pMsg->pid = src_pid;

        //Shift remaining messages
        for(i=nMsg+1; i<g_arThInbox[current_pid].nMsgCnt; i++){
            memcpy(g_arThInbox[current_pid].arMsg+i-1,
                g_arThInbox[current_pid].arMsg+i, sizeof(struct ThothMsg));
        }
        g_arThInbox[current_pid].nMsgCnt--;
    }

    //Check if a process was blocked in attempt to communicate with current_pid,
    //because of full inbox
    for(nPid=0; nPid<MAX_PROCESS_CNT; nPid++)
    {
        if( (g_arThInbox[nPid].bFlags&THOTH_IF_FULL)
        && (g_arThInbox[nPid].pidOverflow==current_pid))
        {
            //Clear the address of the argument which
            //was used in the Send of the blocked process
            struct ThothMsg *pOverflowMsg = g_arThInbox[nPid].pMsgOverflow;

            //Copy the message form the argument into
            //the inbox of the current process
            memcpy(&g_arThInbox[current_pid].arMsg[ g_arThInbox[current_pid].nMsgCnt ],
                pOverflowMsg, sizeof(struct ThothMsg));
            g_arThInbox[current_pid].arMsg[ g_arThInbox[current_pid].nMsgCnt++ ].pid
                = g_arThInbox[nPid].pidOverflow;
            pOverflowMsg->pid = current_pid;
   
            //Mark the new reason for blocking (waiting for Reply)
            g_arThInbox[i].bFlags = THOTH_IF_BLOCKED | THOTH_IF_REPLY;
            g_arThInbox[i].pidOverflow  = current_pid;

            return;
        }
    }
}


//
// Kernel Call - Send a Reply
//
void thothReply(struct ThothMsg *pMsg)
{
    PID dest_pid    = pMsg->pid;
    PID current_pid = tmGetCrProcessID();

    //Check if the target process exists
    if( (dest_pid >= MAX_PROCESS_CNT) || !(tmGetProcessState(dest_pid)&T_STATE__ALIVE) )
    {
        pMsg->pid = PID_INVALID;
        return;
    }
   
    //Check if the target process is waiting for reply
    if( !(g_arThInbox[dest_pid].bFlags&THOTH_IF_REPLY)
    || (g_arThInbox[dest_pid].pidOverflow!=current_pid) )
    {
        pMsg->pid = PID_INVALID;
        return;
    }

    //Check if the target inbox has free space
    if( g_arThInbox[dest_pid].nMsgCnt >= THOTH_INBOXSZ)
    {
        //Clear the address of the argument used
        //by the blocked process when calling Send
        struct ThothMsg *pOverflowMsg = g_arThInbox[dest_pid].pMsgOverflow;

        //Copy the message from argument into the inbox of the current process
        memcpy(pOverflowMsg, pMsg, sizeof(struct ThothMsg));
        pOverflowMsg->pid = current_pid;
   
        //Unblock the process
        g_arThInbox[dest_pid].bFlags = 0;
        tmWakeUp(dest_pid);
        return;
    }

    //Copy the message from the argument to the target inbox
    memcpy(&g_arThInbox[dest_pid].arMsg[ g_arThInbox[dest_pid].nMsgCnt ],
        pMsg, sizeof(struct ThothMsg));
    g_arThInbox[dest_pid].arMsg[ g_arThInbox[dest_pid].nMsgCnt++ ].pid = current_pid;

    pMsg->pid = dest_pid;

    //Unblock the target process
    g_arThInbox[dest_pid].bFlags = 0;
    tmWakeUp(dest_pid);
}