lumiera_/src/proc/engine/job-ticket.hpp

262 lines
8 KiB
C++
Raw Normal View History

/*
JOB-TICKET.hpp - execution plan for render jobs
Copyright (C) Lumiera.org
2012, Hermann Vosseler <Ichthyostega@web.de>
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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef PROC_ENGINE_JOB_TICKET_H
#define PROC_ENGINE_JOB_TICKET_H
#include "proc/common.hpp"
//#include "proc/state.hpp"
2012-02-18 19:42:20 +01:00
#include "proc/engine/job.hpp"
#include "proc/engine/frame-coord.hpp"
//#include "lib/time/timevalue.hpp"
//#include "lib/time/timequant.hpp"
#include "lib/linked-elements.hpp"
#include "lib/iter-adapter.hpp"
#include "lib/util.hpp"
#include <boost/noncopyable.hpp>
#include <stack>
namespace proc {
namespace engine {
//using lib::time::TimeSpan;
//using lib::time::Duration;
//using lib::time::FSecs;
//using lib::time::Time;
using lib::LinkedElements;
using util::isnil;
//
//class ExitNode;
/**
* a job closure represents the execution context of a job.
* This allows us to enqueue simple job-"functions" with the scheduler.
* By virtue of the JobClosure-pointer, embedded into #lumiera_jobDefinition,
* the invocation of such a job may re-gain the full context, including the
* actual ProcNode to pull and further specifics, like the media channel.
*/
class JobClosure
: public lumiera_jobClosure
{
public:
virtual ~JobClosure(); ///< this is an interface
virtual void invokeJobOperation (JobParameter parameter) =0;
virtual void signalFailure (JobParameter parameter) =0;
virtual JobKind getJobKind() const =0;
virtual bool verify (Time nominalJobTime) const =0;
};
/**
* execution plan for pulling a specific exit node.
* Usable as blue print for generating actual render jobs.
* Job tickets are created on demand, specialised for each segment
* of the low-level model, and for each individual feed (corresponding
* to a single model port). Once created, they are final for this segment,
* stored together with the other descriptor objects (ProcNode and WiringDescriptor)
* and finally discarded in bulk, in case that segment of the low-level model becomes
* obsolete and is replaced by a newly built new version of this model segment.
*
* Job tickets are created by a classical recursive descent call on the exit node,
* which figures out everything to be done for generating data from this node.
* To turn a JobTicket into an actual job, we need the additional information
* regarding the precise frame number (=nominal time) and the channel number
* to calculate (in case the actual feed is multichannel, which is the default).
* This way, the JobTicket acts as <i>higher order function:</i> a function
* generating on invocation another, specific function (= the job).
*
* @todo 1/12 WIP-WIP-WIP defining the invocation sequence and render jobs
*/
class JobTicket
: boost::noncopyable
{
struct Provision
{
Provision* next;
};
struct Prerequisite
{
Prerequisite* next;
JobTicket* descriptor;
};
struct Prerequisites ///< per channel
{
Prerequisites* next;
LinkedElements<Prerequisite> requiredJobs_;
};
LinkedElements<Provision> channelConfig_;
LinkedElements<Prerequisites> requirement_;
public:
class ExplorationState;
friend class ExplorationState;
JobTicket()
{
UNIMPLEMENTED ("job representation, planning and scheduling");
}
ExplorationState startExploration() const;
ExplorationState discoverPrerequisites (uint channelNr) const;
Job createJobFor (FrameCoord coordinates);
2012-02-18 19:42:20 +01:00
bool
isValid() const
{
if (channelConfig_.size() != requirement_.size())
return false;
UNIMPLEMENTED ("validity self check");
}
};
class JobTicket::ExplorationState
{
typedef LinkedElements<Prerequisite>::iterator SubTicketSeq;
typedef std::stack<SubTicketSeq> SubTicketStack; //////////////////////////TODO use a custom container to avoid heap allocations
SubTicketStack toExplore_;
public:
2012-09-04 00:17:28 +02:00
ExplorationState() { }
ExplorationState (Prerequisites& prerequisites)
{
if (!isnil (prerequisites.requiredJobs_))
toExplore_.push (prerequisites.requiredJobs_.begin());
}
// using default copy operations
bool
empty() const
{
return toExplore_.empty();
}
void
pullNext()
{
if (empty())
throw lumiera::error::Logic ("Exploration of Job prerequisites floundered. "
"Attempt to iterate beyond the end of prerequisite list"
,lumiera::error::LUMIERA_ERROR_ITER_EXHAUST);
ASSERT (toExplore_.top().isValid());
++(toExplore_.top());
while ( !toExplore_.empty()
&& toExplore_.top().empty())
toExplore_.pop();
ENSURE (empty() || toExplore_.top().isValid());
}
void
push (ExplorationState subExploration)
{
if (subExploration.empty()) return;
pushAllPrerequisites (subExploration.toExplore_);
}
JobTicket*
operator->() const
{
REQUIRE (!empty() && toExplore_.top().isValid());
REQUIRE (toExplore_.top()->descriptor);
REQUIRE (toExplore_.top()->descriptor->isValid());
return toExplore_.top()->descriptor;
}
private:
void
pushAllPrerequisites (SubTicketStack& furtherPrerequisites)
{
REQUIRE (!isnil (furtherPrerequisites));
if (1 == furtherPrerequisites.size())
{
this->toExplore_.push (furtherPrerequisites.top());
}
else
{ // pathological case: several levels of prerequisites
// --> push recursively to retain level ordering
SubTicketSeq deepestLevel (furtherPrerequisites.top());
furtherPrerequisites.pop();
pushAllPrerequisites (furtherPrerequisites);
this->toExplore_.push (deepestLevel);
}
}
};
inline JobTicket::ExplorationState
JobTicket::startExploration() const
{
UNIMPLEMENTED ("somehow build a self-referential pseudo-prerequisite, and seed an ExplorationState with that");
/////////////////////TODO problem is: we need an JobTicket::Prerequisite instance, where the descriptor points to "self" (this JobTicket)
/////////////////////TODO : but this instance needs to reside somewhere at a safe location, since we want to embed an LinkedElements-iterator
/////////////////////TODO : into the ExplorationState. And obviously we do not want that instance in each JobTicket, only in the top level ones
}
inline JobTicket::ExplorationState
JobTicket::discoverPrerequisites (uint channelNr) const
{
REQUIRE (channelNr < requirement_.size());
2012-09-04 00:17:28 +02:00
return ExplorationState (requirement_[channelNr]);
}
}} // namespace proc::engine
#endif