Logo Search packages:      
Sourcecode: dctc version File versions  Download package

sema_master.c

/* DCTC - a Direct Connect text clone for Linux
 * Copyright (C) 2001 Eric Prevoteau
 *
 * sema_master.c: Copyright (C) Eric Prevoteau <www@a2pb.gotdns.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
/*
$Id: sema_master.c,v 1.3 2003/12/28 08:12:38 uid68112 Exp $
*/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <limits.h>
#include <pthread.h>
#include <sched.h>
#include <glib.h>

#include "sema_master.h"

#if !(defined(BSD) && (BSD >= 199103))
       #if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
       /* union semun is defined by including <sys/sem.h> */
       #else
       /* according to X/OPEN we have to define it ourselves */
       union semun {
               int val;                    /* value for SETVAL */
               struct semid_ds *buf;       /* buffer for IPC_STAT, IPC_SET */
               unsigned short int *array;  /* array for GETALL, SETALL */
               struct seminfo *__buf;      /* buffer for IPC_INFO */
       };
       #endif
#endif

#ifndef IPC_ALLOC
#define IPC_ALLOC 0
#endif

#include "uaddr.h"
#include "status.h"

/***********************************************************************/
/* the following functions manage the bandwidth upload limit           */
/* The system is build on 2 semaphores and 1 file                      */
/* 1) the file contains the semaphore key. This allows all DCTC on the */
/*    same computer to share the bandwidth                             */
/* 2) The second side is the managment function itself. It is the 2    */
/*    semaphore. The first semaphore is used to choose which DCTC (if  */
/*    more than one exists) is the "clock" master. The second semaphore*/
/*    is the bandwidth limitation semaphore (BLS). Despite the fact it */
/*    is a semaphore, it acts more like a set of token.                */
/*    a) Every second, the clock master resets BLS to a given value    */
/*      (for example, 8 for 4KB/s (1=512byte/s).                       */
/*    b) When a DCTC wants to sends something, it must first acquire 1 */
/*       or more token (never acquire more than 1 at the same time,    */
/*       if you need 3 for example, you must acquire 3 times 1 token,  */
/*       this is needed if for example the speed is limited to 4KB/s   */
/*       (max BLS=8) and you want to send 8KB (you must have 16 token).*/
/* Addendum: The current speed limit is stored inside a 3rd semaphore  */
/*       This value must be shared by all DCTC (because we don't know  */
/*       which one is the clock master. A piece of shared memore is    */
/*       sufficient but is too heavy to set up. Thus, to simplify, the */
/*       3rd semaphore value is copied into the 2nd by the clock master*/
/*       every second.                                                 */
/***********************************************************************/

/******************************/
/* initialize semaphore array */
/*************************************************************************************/
/* input: keyfile : if not exists, it is created and the semaphore key is put inside */
/*                  if exists but the semaphore key inside is invalid, same as above */
/*                  if exists and contains a valid key, nothing is done              */
/*        spd_limit is the default speed limit (in number of 512bytes slice)         */
/*************************************************************************************/
/* output: 0=ok, !=0=error                                              */
/*         on success, *cur_semid is the semaphore id to use with semop */
/************************************************************************/
int do_sema_init(char *keyfile, int *cur_semid, int spd_limit, int dl_spd_limit, int gath_spd_limit, int ttl_up_slot)
{
      int fd;
      key_t key;

      int semid;

      fd=open(keyfile,O_CREAT|O_WRONLY|O_EXCL,0600);        /* create the file if not exists */
      if(fd==-1)
      {
            if(errno==EEXIST)
            {
                  printf("file exists.\n");
                  fd=open(keyfile,O_RDWR);
                  if(fd==-1)
                  {
                        perror("open(R).");
                        return 1;
                  }

                  if(read(fd,&key,sizeof(key))!=sizeof(key))
                  {
                        close(fd);
                        /* fail to read current key, create a new file and force generation of new sema */
                        create_new_sema:
                        printf("creating new sema.\n");
                        fd=open(keyfile,O_CREAT|O_WRONLY,0600); 
                        if(fd==-1)
                        {
                              perror("open(W2).");
                              return 1;
                        }
                  
                  }
                  else
                  {
                        close(fd);
                        /* a key exist */
                        semid=semget(key,0,IPC_ALLOC);
                        if(semid==-1)
                              goto create_new_sema;
                        printf("current sema found.\n");
                        goto eofunc;
                  }
            }
            else
            {
                  perror("open(W).");
                  return 1;
            }
      }
      
      printf("creating.\n");
      key=rand();
      while((semid=semget(key,SEMA_ARRAY_LEN,IPC_CREAT|IPC_EXCL|0600))==-1)   /* allocate 3 semaphores */
            key++;

      printf("semid=%d\n",semid);
      printf("created %08X.\n",key);

      if(write(fd,&key,sizeof(key))!=sizeof(key))
      {
            close(fd);
            unlink(keyfile);
      }

      /* initialize sema array */
      {
            union semun v;

            v.val=1;
            if(semctl(semid,0,SETVAL,v)==-1)
                  perror("semctl0");

            /* upload speed values */
            v.val=0;
            if(semctl(semid,1,SETVAL,v)==-1)
                  perror("semctl1");
            v.val=spd_limit;
            if(semctl(semid,2,SETVAL,v)==-1)
                  perror("semctl2");

            /* download speed values */
            v.val=0;
            if(semctl(semid,3,SETVAL,v)==-1)
                  perror("semctl3");
            v.val=dl_spd_limit;
            if(semctl(semid,4,SETVAL,v)==-1)
                  perror("semctl4");

            /* download speed values */
            v.val=0;
            if(semctl(semid,5,SETVAL,v)==-1)
                  perror("semctl5");
            v.val=gath_spd_limit;
            if(semctl(semid,6,SETVAL,v)==-1)
                  perror("semctl6");

            /* initialize upload slot control */
            v.val=1;
            if(semctl(semid,7,SETVAL,v)==-1)
                  perror("semctl7");
            v.val=ttl_up_slot;
            if(semctl(semid,8,SETVAL,v)==-1)
                  perror("semctl8");
            v.val=0;                                              /* at the beginning, all upload slots are free */
            if(semctl(semid,9,SETVAL,v)==-1)
                  perror("semctl9");
      }
      close(fd);
      eofunc:
      *cur_semid=semid;
      return 0;
}

/******************************************************************/
/* this is the thread acting as clock                             */
/* every second, it resets the 2nd semaphore to its initial value */
/******************************************************************/
static void *sema_master(void *dm_val)
{
      int semid=(int)dm_val;
      FLAG1_STRUCT fs1;

      if(sizeof(fs1)!=sizeof(unsigned long int))
            fprintf(stderr,"FLAG1_STRUCT has an invalid size (%d), result can be erroneous\n",sizeof(fs1));
      /* set the is_clock_master flag of the gstatus */
      fs1.full=GET_GSTATUS_FLAG1();
      fs1.bf.is_clock_master=1;
      SET_GSTATUS_FLAG1(fs1.full);

      while(1)
      {
            union semun v;
            int i;

            /* reset upload/download and gather speed */
            for(i=0;i<3;i++)
            {
                  /* reset speed */
                  v.val=semctl(semid,2+2*i,GETVAL,v);
                  if(v.val==-1)
                        v.val=SEMVMX;

                  if(semctl(semid,1+2*i,SETVAL,v)==-1)
                        perror("semctl1");
            }
            sleep(1);
      }
}

/**********************************************/
/* create clock thread                        */
/* on error, the master semaphore is released */
/**********************************************/
static void create_sema_master(int semid)
{
      static pthread_t thread_id; /* this variable must exist as long as the thread exist */
      pthread_attr_t thread_attr;

   pthread_attr_init (&thread_attr);
   pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
   if(pthread_create(&thread_id,&thread_attr, (void*)sema_master,(void*)semid)!=0)
      {
            /* if the creation of the clock thread fails, release the master sema */
            /* else nobody will try to create a new clock and all xfers will hang */
            struct sembuf sb={0,+1,SEM_UNDO};               /* master sema */
            semop(semid,&sb,1);
      }
      else
      {
            /* to reduce duplicated code, the DCTC being the clock master is also the one performing UADDR action */
            create_uaddr_thread();
      }
      pthread_attr_destroy(&thread_attr);
}

/******************************************************************************************/
/* to avoid forever hanging of download, we must regularly check if a clock master exists */
/******************************************************************************************/
/* output: 1= master created, 0= master not created */
/****************************************************/
int check_sema_master(int semid)
{
      struct sembuf sb={0,-1,IPC_NOWAIT|SEM_UNDO};                /* master sema */

      if(semop(semid,&sb,1)==0)
      {
            /* to get slice, the function checks if the clock thread still runs */
            create_sema_master(semid);
            fprintf(stderr,"I am the new master.\n");
            return 1;
      }
      else
      {
            perror("a master already runs");
            return 0;
      }
}

/************************/
/* get 1 512Bytes slice */
/*******************************************/
/* the function ends when it has the slice */
/*******************************************/
void get_slice(int semid, SPD_SEMA semnum)
{
      while(1)
      {
            struct sembuf local={0,-1,0};       /* slave sema */

            local.sem_num=semnum;
            if(semop(semid,&local,1)==0)
            {
                  /* we have what we want */
                  return;
            }
      }
}

/*************************/
/* get nb 512Bytes slice */
/*******************************************/
/* the function ends when it has the slice */
/*******************************************/
void get_ul_slices(int semid,int nb)
{
#ifdef _POSIX_PRIORITY_SCHEDULING
      sched_yield();
#endif

      while(nb>0)
      {
            get_slice(semid,UL_SEMA);
            nb--;
      }
}

/************************/
/* get nb 1KBytes slice */
/*******************************************/
/* the function ends when it has the slice */
/*******************************************/
void get_dl_slices(int semid,int nb)
{
#ifdef _POSIX_PRIORITY_SCHEDULING
      sched_yield();
#endif

      while(nb>0)
      {
            get_slice(semid,DL_SEMA);
            nb--;
      }
}

/************************/
/* get nb 8KBytes slice */
/*******************************************/
/* the function ends when it has the slice */
/*******************************************/
void get_gather_slices(int semid,int nb)
{
      while(nb>0)
      {
            get_slice(semid,GATHER_SEMA);
            nb--;
      }
}

/******************************/
/* lock the UL slot controler */
/******************************/
void lock_ul_slot_controler(int semid)
{
      struct sembuf get_ul_ctrl={UL_SLOT_SEMA,-1,SEM_UNDO};
      /* lock the UL slot controler */
      semop(semid,&get_ul_ctrl,1);
}

/*********************************/
/* release the UL slot controler */
/*********************************/
void release_ul_slot_controler(int semid)
{
      struct sembuf release_ul_ctrl={UL_SLOT_SEMA,+1,SEM_UNDO};
      /* release the UL slot controler */
      semop(semid,&release_ul_ctrl,1);
}

/*********************************************************/
/* get upload slot values (total ul slot and #busy slots */
/*********************************************************/
/* WARNING: this function locks the UL controler itself */
/********************************************************/
void get_ul_slot_values(int semid,int *ttl_slot, int *busy_slot)
{
      union semun v;
      lock_ul_slot_controler(semid);

      *ttl_slot=semctl(semid,UL_SLOT_TTL_SEMA,GETVAL,v);
      *busy_slot=semctl(semid,UL_SLOT_BUSY_SEMA,GETVAL,v);

      release_ul_slot_controler(semid);
}

/*******************************************************************/
/* count the number of free slots. This function performs NO LOCK  */
/* and is only design to return the number of free slots at a time */
/* If the number of free slot is negative, it is rounded to 0.     */
/*******************************************************************/
int number_of_free_slot(int semid)
{
      union semun v;
      int ttl, busy;
      int free_slt;

      ttl=semctl(semid,UL_SLOT_TTL_SEMA,GETVAL,v);
      busy=semctl(semid,UL_SLOT_BUSY_SEMA,GETVAL,v);

      free_slt=ttl-busy;
      if(free_slt<0)
            free_slt=0;
      return free_slt;
}

static int nb_local_ul=0;                 /* number of upload slots used by this client */

/******************************/
/* try to get one upload slot */
/************************************************/
/* output: 0= fail to get slot, 1=slot obtained */
/********************************************************/
/* WARNING: this function locks the UL controler itself */
/********************************************************/
int try_to_get_ul_slot(int semid)
{
      union semun v;
      int ttl_slot, busy_slot;
      int ret=0;

      lock_ul_slot_controler(semid);

      ttl_slot=semctl(semid,UL_SLOT_TTL_SEMA,GETVAL,v);
      busy_slot=semctl(semid,UL_SLOT_BUSY_SEMA,GETVAL,v);
      if(busy_slot<ttl_slot)
      {
            /* we must use this command because the previous code */
            /* does not free the ul slot if the program ends */
            struct sembuf get_one_ul_slot={UL_SLOT_BUSY_SEMA,+1,SEM_UNDO};

            ret=1;
            if(semop(semid,&get_one_ul_slot,1)==-1)
            {
                  perror("try_to_get_ul_slot");
                  ret=0;
            }
      }

      if(ret)
            nb_local_ul++;

      release_ul_slot_controler(semid);

      if(ret)
      {     /* set the number of uploads of this client after releasing the ul_slot_controler */
            SET_GSTATUS_UL(nb_local_ul);
      }
      return ret;
}

/************************************************/
/* get an upload slot even if none is available */
/********************************************************/
/* WARNING: this function locks the UL controler itself */
/********************************************************/
void force_get_ul_slot(int semid)
{
      struct sembuf get_one_ul_slot={UL_SLOT_BUSY_SEMA,+1,SEM_UNDO};

      lock_ul_slot_controler(semid);
      if(semop(semid,&get_one_ul_slot,1)==-1)
      {
            perror("force_get_ul_slot");
      }
      nb_local_ul++;

      release_ul_slot_controler(semid);

      /* set the number of uploads of this client after releasing the ul_slot_controler */
      SET_GSTATUS_UL(nb_local_ul);
}


/*******************************************************/
/* free an upload slot allocated by try_to_get_ul_slot */
/*******************************************************/
void free_one_ul_slot(int semid)
{
      union semun v;
      int busy_slot;
      lock_ul_slot_controler(semid);
      busy_slot=semctl(semid,UL_SLOT_BUSY_SEMA,GETVAL,v);
      if(busy_slot>0)
      {
            struct sembuf free_one_ul_slot_op={UL_SLOT_BUSY_SEMA,-1,SEM_UNDO};
            if(semop(semid,&free_one_ul_slot_op,1)==-1)
            {
                  perror("free_one_ul_slot");
            }
            nb_local_ul--;
      }
      else
      {
            fprintf(stderr,"free_one_ul_slot: no slot is busy\n");
      }
      release_ul_slot_controler(semid);

      /* set the number of uploads of this client after releasing the ul_slot_controler */
      SET_GSTATUS_UL(nb_local_ul);
}

/*********************************/
/* set the number of upload slot */
/*********************************/
void set_number_of_ul_slot(int semid, unsigned int number_of_slot)
{
      union semun v;
      v.val=number_of_slot;

      lock_ul_slot_controler(semid);
      if(semctl(semid,UL_SLOT_TTL_SEMA,SETVAL,v)==-1)
      {
            perror("free_one_ul_slot");
      }
      release_ul_slot_controler(semid);
}

/*********************************/
/* set the number of upload slot */
/*********************************/
int get_number_of_ul_slot(int semid)
{
      union semun v;
      int ttl_slot;

      lock_ul_slot_controler(semid);
      ttl_slot=semctl(semid,UL_SLOT_TTL_SEMA,GETVAL,v);
      release_ul_slot_controler(semid);

      return ttl_slot;
}


Generated by  Doxygen 1.6.0   Back to index