Changeset - 1aeb6d2aebd0
[Not reviewed]
default
0 3 0
LordOfWar - 16 years ago 2009-10-24 17:01:27

created an almost working skeletal framework for a smarter frame_finder, which will assign frames in a much less linear order
3 files changed with 93 insertions and 1 deletions:
0 comments (0 inline, 0 general)
src/server/distrend.c
Show inline comments
 
@@ -23,96 +23,97 @@
 
/*
 
  To get getline():
 
 */
 
#define _GNU_SOURCE 1
 

	
 
#include "execio.h"
 
#include "options.h"
 
#include "distrenjob.h"
 
#include "protocol.h"
 
#include "slavefuncs.h"
 
#include "asprintf.h"
 

	
 
#include <confuse.h>
 
#include <stdio.h>
 
#include <stdlib.h>
 
#include <malloc.h>
 
#include <unistd.h>
 
#include <time.h>
 
#include <sys/stat.h>
 
#include <string.h>
 

	
 
#include <libxml/xmlmemory.h>
 
#include <libxml/parser.h>
 
#include <libxml/tree.h>
 
#include <libxml/encoding.h>
 
#include <libxml/xmlwriter.h>
 
#include <libxml/xmlreader.h>
 

	
 
/* ******************* Structs ************************ */
 

	
 
// Gets config info from confs
 
struct distrend_config
 
{
 
  cfg_t *mycfg;
 
  struct options_common *options;
 
  struct distrend_listen **listens; /*< Null terminated array of structs */
 
  char *datadir;
 
};
 

	
 
struct general_info {
 
  short int jobs_in_queue; //
 
  unsigned short int free_clients;
 
  unsigned short int rendering_clients;//
 
  unsigned short int total_finished_jobs; //
 
  unsigned int total_frames_rendered; //
 
  unsigned int highest_jobnum;
 
  short int hibernate;
 
  unsigned int timestamp;
 
  unsigned long int total_render_power;
 
} general_info;
 

	
 

	
 

	
 
/*
 
  internally defined funcs's prototypes @TODO: Make all functions nice and proper */
 
void distrenjob_remove(struct distrenjob *head, struct distrenjob *bj);
 

	
 
struct distrenjob *distrenjob_get(struct distrenjob *head, jobnum_t jobnum);
 
void distrenjob_enqueue(struct distrenjob *head, struct distrenjob *job);
 
void mortition(struct distrenjob *head, struct distrenjob *job);
 
int makeJobDataXML(struct distrenjob *job);
 
int updateJobListXML(struct distrenjob *head);
 
int createQueueFromXML(struct distrenjob *head);
 
int reCreateQueueFromXML(struct distrenjob *head, xmlDocPtr doc, xmlNodePtr current);
 
void updateGeneralInfo();
 
void importGeneralInfo();
 
int updateJobStatsXML(struct distrenjob *job);
 

	
 

	
 
/* Global Vars, eliminate these */
 
jobnum_t jobnum = 0; // The next job number to create in the queue
 
int hcfjob; // Highest consecutively finished job
 
int highest_jobnum; // The job number of the most recently created job, this is used when creating new jobs
 

	
 

	
 
/* ********************** Functions ************************* */
 

	
 
/** Dumps all data in RAM to an xml file (such as current jobs, etc) which is parsed by start_data. Remember to invoke this before shutting down! */
 
int xml_dump()
 
{
 
  return 0;
 
}
 
/**
 
   Performs command stored in a client's request.
 
*/
 
int distrend_do()
 
{
 
  return 0;
 
}
 
/**
 
   Accepts a client's connection
 
 */
 
void distrend_accept()
 
{
 

	
 
}
 
/**
 
@@ -373,96 +374,185 @@ void change_job_priority(struct distrenj
 
*/
 
int find_jobframe(struct distrenjob *head, struct distrenjob **job, struct frameset **frame)
 
{
 
  if(general_info.hibernate)
 
    return 1;
 

	
 
  unsigned int frame_counter;
 
  unsigned short int found;
 

	
 
  struct distrenjob *distrenjob_ptr;
 

	
 
  found = 0;
 
  /* iterate through jobs from first to last */
 
  for(distrenjob_ptr = head->next; distrenjob_ptr && !distrenjob_ptr->hibernate; distrenjob_ptr = distrenjob_ptr->next)
 
  {
 
    for(frame_counter = (distrenjob_ptr->prev_frame_index + 1); frame_counter < distrenjob_ptr->total_frames; frame_counter ++)
 
    {
 
      if(distrenjob_ptr->frameset[frame_counter].status == FRAMESETSTATUS_UNASSIGNED) // jobframe found
 
      {
 
    	found = 1;
 
    	distrenjob_ptr->frameset[frame_counter].status = FRAMESETSTATUS_ASSIGNED;
 
    	distrenjob_ptr->frameset[frame_counter].start_time = clock();
 
    	distrenjob_ptr->assigned_frames++;
 
    	distrenjob_ptr->prev_frame_index = frame_counter;
 
    	updateJobStatsXML(distrenjob_ptr);
 
      }
 

	
 
      if(found)
 
    	break;
 
    }
 

	
 
    if(found)
 
      break;
 
  }
 

	
 
  if(!found)
 
    {
 
      fprintf(stderr, "No more jobs to render\n");
 
      sleep(1); /*< @todo eliminate the need for this line*/
 
      return 1;
 
    }
 

	
 
  *job = distrenjob_ptr;
 
  *frame = &distrenjob_ptr->frameset[frame_counter];
 

	
 
  return 0;
 
}
 

	
 
int find_jobframe_from_job(struct distrenjob *head, struct distrenjob *distrenjob_ptr, struct distrenjob **job, struct frameset **frame)
 
{
 
  unsigned int frame_counter;
 
  unsigned short int found;
 

	
 
  found = 0;
 
  for(frame_counter = (distrenjob_ptr->prev_frame_index + 1); frame_counter < distrenjob_ptr->total_frames; frame_counter ++)
 
  {
 
    if(distrenjob_ptr->frameset[frame_counter].status == FRAMESETSTATUS_UNASSIGNED) // jobframe found
 
      {
 
    	found = 1;
 
		distrenjob_ptr->frameset[frame_counter].status = FRAMESETSTATUS_ASSIGNED;
 
		distrenjob_ptr->frameset[frame_counter].start_time = clock();
 
		distrenjob_ptr->assigned_frames++;
 
		distrenjob_ptr->prev_frame_index = frame_counter;
 
		updateJobStatsXML(distrenjob_ptr);
 
      }
 

	
 
    if(found)
 
      break;
 
  }
 

	
 

	
 
  if(!found)
 
    {
 
      fprintf(stderr, "No more frames in this job number %d", distrenjob_ptr->jobnum);
 
      sleep(1); /*< @todo eliminate the need for this line*/
 
      return 1;
 
    }
 

	
 
  *job = distrenjob_ptr;
 
  *frame = &distrenjob_ptr->frameset[frame_counter];
 

	
 
  return 0;
 
}
 

	
 
// find a frame to render when the job that the last frame was for no longer exists
 
int find_jobframe_new(struct distrenjob *head, int rend_pwr, struct distrenjob **job, struct frameset **frame)
 
{
 
  if(general_info.hibernate)
 
    return 1;
 

	
 
  unsigned short int found;
 
  struct distrenjob *job_to_render;
 

	
 
  struct distrenjob *distrenjob_ptr;
 

	
 
  found = 0;
 
  /* iterate through jobs from first to last */
 
  for(distrenjob_ptr = head->next; distrenjob_ptr && !distrenjob_ptr->hibernate; distrenjob_ptr = distrenjob_ptr->next)
 
  {
 

	
 
	  job_to_render = distrenjob_ptr;
 
  }
 

	
 
  find_jobframe_from_job(head, job_to_render, job, frame);
 

	
 
  if(!found)
 
    {
 
      fprintf(stderr, "No more jobs to render\n");
 
      sleep(1); /*< @todo eliminate the need for this line*/
 
      return 1;
 
    }
 

	
 
  return 0;
 
}
 

	
 
// gets a frame to render from the same job that the previously rendered frame was from
 
int find_jobframe_again(struct distrenjob *head, int jobnum, int rend_pwr, struct distrenjob **job, struct frameset **frame)
 
{
 
  if(general_info.hibernate)
 
    return 1;
 

	
 
  struct distrenjob *distrenjob_ptr;
 

	
 
  distrenjob_ptr = distrenjob_get(head, jobnum);
 

	
 
  if(!distrenjob_ptr)
 
    {
 
      fprintf(stderr, "Job number %d has been finished, finding new job\n", jobnum);
 
      sleep(1); /*< @todo eliminate the need for this line*/
 
      return 1;
 
    }
 

	
 
  find_jobframe_new(head, rend_pwr, job, frame);
 

	
 
  return 0;
 
}
 

	
 
/** Checks for dead, latent, or stale slaves */
 
void frame_watchdog(struct distrenjob *distrenjob_head)
 
{
 
  struct distrenjob *distrenjob_ptr;
 
  unsigned int counter;
 

	
 
  for(distrenjob_ptr = distrenjob_head->next; distrenjob_ptr; distrenjob_ptr = distrenjob_ptr->next)
 
  /* iterate through jobs */
 
  {
 
    /* if the job has been started, checks by seeing if either to first or second frame has been started */
 
    if(distrenjob_ptr->frameset[0].status != FRAMESETSTATUS_UNASSIGNED || distrenjob_ptr->frameset[1].status != FRAMESETSTATUS_UNASSIGNED)
 
      /* iterate through all frames for this job: */
 
      for(counter = 0; counter < distrenjob_ptr->total_frames; counter ++)
 
	    /*watchdog_forgiveness = seconds of forgiveness before frame is re-assigned: */
 
        if((distrenjob_ptr->frameset[counter].start_time + distrenjob_ptr->watchdog_forgiveness) < clock())
 
        {
 
            /*
 
            If frame is not completed within the number of seconds specified by watchdog_forgiveness
 
            Then change the frame status to unassigned
 
            */
 
            distrenjob_ptr->frameset[counter].status = FRAMESETSTATUS_UNASSIGNED;
 
            distrenjob_ptr->assigned_frames--;
 
        }
 

	
 
    updateJobStatsXML(distrenjob_ptr);
 
  }
 
}
 

	
 
/**
 
   Finds a distrenjob struct based on the jobnum
 
   @arg jobnum job number to search for
 
   @return NULL on job doesn't exist
 
 */
 
struct distrenjob *distrenjob_get(struct distrenjob *head, jobnum_t jobnum)
 
{
 
  struct distrenjob *distrenjob_ptr;
 

	
 
  /*
 
    The conditions of the for loop will leave distrenjob_ptr at NULL if the end of the list is reached. It will leave it pointing to the correct job if it is found.
 
   */
 
  for(distrenjob_ptr = head;
 
      distrenjob_ptr
 
        && distrenjob_ptr->jobnum != jobnum;
 
      distrenjob_ptr = distrenjob_ptr->next);
 

	
 
  return distrenjob_ptr;
 
}
 

	
src/server/distrenjob.c
Show inline comments
 
@@ -20,96 +20,97 @@
 
#include "slavefuncs.h"
 
#include "asprintf.h"
 

	
 
#include <libxml/tree.h>
 
#include <libxml/parser.h>
 

	
 
void distrenjob_free(struct distrenjob **distrenjob)
 
{
 
  struct distrenjob *dj;
 

	
 
  dj = *distrenjob;
 
  xmlFree(dj->name);
 
  xmlFree(dj->submitter);
 
  xmlFree(dj->email);
 

	
 
  free(dj->frameset);
 

	
 
  free(dj);
 
  *distrenjob = NULL;
 
}
 

	
 
int distrenjob_new(struct distrenjob **distrenjob)
 
{
 
  struct distrenjob *dj;
 

	
 
  dj = malloc(sizeof(struct distrenjob));
 
  if(!dj)
 
    {
 
      /* try to catch code that doesn't respect return values
 
       faster: */
 
      *distrenjob = NULL;
 
      return 1;
 
    }
 
  *distrenjob = dj;
 

	
 
  dj->next = NULL;
 
  dj->name = (char *)NULL;
 
  dj->submitter = (char *)NULL;
 
  dj->email = (char *)NULL;
 
  dj->jobnum = 0; /*< @todo there should be a central jobnum allocator and a way to save the maximum jobnumber allocated */
 
  dj->priority = 0;
 
  dj->width = 0;
 
  dj->height = 0;
 
  dj->completed_frames = 0;
 
  dj->assigned_frames = 0;
 
  dj->total_render_time = 0;
 
  dj->hibernate = 0;
 
  dj->prev_frame_index = -1;
 
  dj->assigned_render_power = 0;
 
  dj->watchdog_forgiveness = 3600; // initialize watchdog forgiveness at 1 hour
 
  dj->frameset = (struct frameset *)NULL; /*< @todo does frameset need to be initialized here? */
 

	
 
  return 0;
 
}
 

	
 
/**
 
   writes struct from xml
 
*/
 
int xml2distrenjob(struct distrenjob **distrenjob, char *pathtoxml)
 
{
 
  struct distrenjob *dj;
 

	
 
  xmlDocPtr xmldoc;
 
  xmlNodePtr xmlnode;
 
  xmlChar *xmlchar;
 

	
 
  xmlXPathContextPtr xmlxpathcontext;
 

	
 
  int tmp;
 

	
 
  if(distrenjob_new(distrenjob))
 
    return 1;
 
  dj = *distrenjob;
 

	
 
  xmldoc = xmlReadFile(pathtoxml, NULL, XML_PARSE_PEDANTIC);
 
  if(!xmldoc)
 
    {
 
      /**
 
	 @todo are we able to depend on libxml2's printed errors or
 
	 channel them into syslog output (eventually)? Currently,
 
	 this error is repetitious of a libxml2 error printed on stderr
 
	 for us.
 
       */
 
      fprintf(stderr, "error reading XML file ``%s''\n", pathtoxml);
 

	
 
      distrenjob_free(distrenjob);
 
      return 2;
 
    }
 

	
 
  xmlxpathcontext = xmlXPathNewContext(xmldoc);
 
  xmlnode = xml_quickxpath(xmlxpathcontext, (xmlChar *)"/distren/job");
 
  if(!xmlnode)
 
    {
 
      distrenjob_free(distrenjob);
 
      return 3;
 
    }
 

	
src/server/distrenjob.h
Show inline comments
 
@@ -4,97 +4,98 @@
 
  This file is a part of DistRen.
 

	
 
  DistRen is free software: you can redistribute it and/or modify
 
  it under the terms of the GNU Affero General Public License as published by
 
  the Free Software Foundation, either version 3 of the License, or
 
  (at your option) any later version.
 

	
 
  DistRen 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 Affero General Public License for more details.
 

	
 
  You should have received a copy of the GNU Affero General Public License
 
  along with DistRen.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
*/
 

	
 
#ifndef _DISTREN_DISTRENJOB_H
 
#define _DISTREN_DISTRENJOB_H
 

	
 
/**
 
   This file stores the distrenjob and frameset structs and prototypes for some functions to manipulate/use these.
 
 */
 

	
 
#include <time.h> /* clock_t, time_t */
 

	
 
typedef unsigned int jobnum_t;
 

	
 
/**
 
   Stores Blender Job Info
 
*/
 
struct distrenjob {
 
  struct distrenjob *next; /*< next will be NULL unless if there is another distrenjob */
 
  short int type; // 1:Blender, 2:something else
 
  char *name;
 
  char *submitter;
 
  char *email; /*< This should be looked up based on the value of submitter, not stored in this struct */
 
  jobnum_t jobnum;
 
  int width; // width in pixels of the frames to be rendered
 
  int height; //height in pixels of the frames to be rendered
 
  int priority;  // 1 is lowest, 10 is highest, 0 means the job is done
 
  int completed_frames; // number of completed frames for stats/etc
 
  int assigned_frames; // number of assigned frames (that are not yet completed) for stats/etc
 
  int total_frames; // how many frames are in the animation for stats/etc (unassigned frames)
 
  int watchdog_forgiveness; // how many seconds till the frame is re-assigned (if client computer crashes etc);
 
  short int hibernate;
 
  int prev_frame_index; // the position in the array of the last frame that was assigned
 
  time_t total_render_time; // total seconds of time spent on all the completed frames
 
  char *output_format; /*< currently is the file extention of the request output format. @todo make this mime-type based/not a string */
 
  char *output_format; /*< currently is the file extension of the request output format. @todo make this mime-type based/not a string */
 
  unsigned long int assigned_render_power;
 
  struct frameset *frameset;
 
};
 

	
 

	
 

	
 
/**
 
   Frameset Structure
 
*/
 
enum framesetstatus
 
  {
 
    FRAMESETSTATUS_CANCELED, /*< The use has canceled this frame */
 
    FRAMESETSTATUS_UNASSIGNED, /*< The frame has not been assigned */
 
    FRAMESETSTATUS_ASSIGNED, /*< The frame has been assigned */
 
    FRAMESETSTATUS_DONE /*< The frame has completed rendering and the slave has returned the product to me */
 
  };
 

	
 
struct frameset {
 
  int num; /*< frame number to render */
 
  char slave_name; /*< user that frame is assigned to */
 
  enum framesetstatus status; /*< status of frame, 0= unassigned, 1= taken, 2= done */
 
  clock_t start_time; /*< time the frame was started */
 
}; /* Frameset array is generated by status_report_generator() */
 

	
 

	
 
/*
 
related functions
 
*/
 

	
 
/**
 

	
 
   @param distrenjob the address where we will store the pointer of a malloc()ed
 
   distrenjob struct.
 
   @param pathtoxml filename/pathname of the xml file to be read into a distrenjob struct
 
 */
 
int xml2distrenjob(struct distrenjob **distrenjob, char *pathtoxml);
 

	
 
/**
 
   support function for xml2distrenjob() to help cleaning up a
 
   struct distrenjob when it is incompletely initialized.
 
   Also acts as a general-purpose struct distrenjob free()er ;-)
 
 */
 
void distrenjob_free(struct distrenjob **distrenjob);
 

	
 
/**
 
   initializes an empty, pointless struct distrenjob. This
 
   DOES run malloc() for you. It could return nonzero on error,
 
   but there are no errors to have yet (except for nomem). This
 
   sets all char* to NULL.
0 comments (0 inline, 0 general)