This is my initial definition of a new internal job control api. I am working on this as a part of the google summer of code. These patches contain the core job control api and deal only with managing individual jobs. I am currently working on writing code using this api to manage jobs in domains, in such a way that I will be able to replace the current job control code in qemu and libxl. Ultimately I will use this to implement job control in the storage driver which is my ultimate goal for the summer of code. --- src/Makefile.am | 1 + src/util/virjobcontrol.c | 574 +++++++++++++++++++++++++++++++++++++++++++++++ src/util/virjobcontrol.h | 342 ++++++++++++++++++++++++++++ 3 files changed, 917 insertions(+) create mode 100644 src/util/virjobcontrol.c create mode 100644 src/util/virjobcontrol.h diff --git a/src/Makefile.am b/src/Makefile.am index 2b9ac61..77de0e7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -118,6 +118,7 @@ UTIL_SOURCES = \ util/virinitctl.c util/virinitctl.h \ util/viriptables.c util/viriptables.h \ util/viriscsi.c util/viriscsi.h \ + util/virjobcontrol.h util/virjobcontrol.c \ util/virjson.c util/virjson.h \ util/virkeycode.c util/virkeycode.h \ util/virkeyfile.c util/virkeyfile.h \ diff --git a/src/util/virjobcontrol.c b/src/util/virjobcontrol.c new file mode 100644 index 0000000..04a5246 --- /dev/null +++ b/src/util/virjobcontrol.c @@ -0,0 +1,574 @@ +/* + * virjobcontrol.c Core implementation of job control + * + * Copyright (C) 2014 Tucker DiNapoli + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Tucker DiNapoli + */ + +#include <config.h> + +#include "virjobcontrol.h" +#include "viralloc.h" +#include "virtime.h" +#include "virlog.h" +VIR_LOG_INIT("virjobcontrol"); + +VIR_ENUM_IMPL(virJob, 4, + "none", + "query", + "modify", + "destroy", +); +/* + No files other then this and virjobcontrol.c should need to + have access to the core implmentation of jobs. The code in these + files is intended to serve as a base for job control independent of + drivers. +*/ + +#define LOCK_JOB(job) \ + virMutexLock(&job->lock) +#define UNLOCK_JOB(job) \ + virMutexUnlock(&job->lock) +#define LOCK_JOB_INFO(job) \ + virMutexLock(&job->info->lock) +#define UNLOCK_JOB_INFO(job) \ + virMutexUnlock(&job->info->lock) +#define GET_CURRENT_TIME(time) \ + if (virTimeMillisNow(&time) < 0) { \ + return -1; \ + } + + +#define CHECK_FLAG_ATOMIC(job, flag) (virAtomicIntGet(&job->flags) & VIR_JOB_FLAG_##flag) +#define CHECK_FLAG(job, flag) (job->flags & VIR_JOB_FLAG_##flag) +#define SET_FLAG_ATOMIC(job, flag) (virAtomicIntOr(&job->flags, VIR_JOB_FLAG_##flag)) +#define SET_FLAG(job, flag) (job->flags |= VIR_JOB_FLAG_##flag) +#define UNSET_FLAG_ATOMIC(job, flag) (virAtomicIntAnd(&job->flags, (~VIR_JOB_FLAG_##flag))) +#define UNSET_FLAG(job, flag) (job->flags &= (~VIR_JOB_FLAG_##flag)) +#define CLEAR_FLAGS_ATOMIC(job) (virAtomicIntSet(&job->flags, VIR_JOB_FLAG_NONE)) +#define CLEAR_FLAGS(job) (job->flags = VIR_JOB_FLAG_NONE) + +typedef struct _jobHashEntry { + virJobID id; + virJobObjPtr job; + struct _jobHashEntry *next; +} jobHashEntry; + +typedef struct _jobHash { + jobHashEntry **table; + size_t size; + size_t num_entries; + virRWLock lock; +} jobHash; + +/* Using this incures a cost on every call to a job control function + that uses the job control hash table, but it means that no one using + job control needs to call an initialization function to use it. + + The other option would be to have a function: + virJobControlInit(void) + { + return virOnce(job_once, jobControlInit); + } + and require anyone using job control to call it. + */ +static struct _jobHash *job_hash; /* Hash table that contians all current jobs */ +static int init_err = 0; +static virOnceControl job_once = VIR_ONCE_CONTROL_INITIALIZER; +static void +jobControlInit(void) +{ + if (VIR_ALLOC_QUIET(job_hash) < 0) { + init_err = 1; + } + if (virRWLockInit(&job_hash->lock) < 0) { + init_err = 1; + } + if (VIR_ALLOC_N_QUIET(job_hash->table, 16) <0) { + init_err = 1; + } +} + +/* global job id + + Because it gets incremented before it's first use 0 is + never a valid job id + + Only ever touched with atomic instructions. +*/ +static unsigned int global_id = 0; + + + +/* Simple hash table implementation for jobs. + It is keyed by job id's which are just integers so there isn't acutally + a hash function. Using the existing hash table in libvirt would be overkill. +*/ + +#ifndef VIR_JOB_REHASH_THRESHOLD +#define VIR_JOB_REHASH_THRESHOLD 0.8 +#endif + +#ifndef VIR_JOB_BUCKET_LEN +#define VIR_JOB_BUCKET_LEN 5 +#endif + +#ifndef VIR_JOB_GROWTH_FACTOR +#define VIR_JOB_GROWTH_FACTOR 2 +#endif + +#define READ_LOCK_TABLE(ht) virRWLockRead((virRWLockPtr)&ht->lock) +#define WRITE_LOCK_TABLE(ht) virRWLockWrite((virRWLockPtr)&ht->lock) +#define UNLOCK_TABLE(ht) virRWLockUnlock((virRWLockPtr)&ht->lock) + +static int jobHashRehash(jobHash *ht); +#define maybe_rehash(ht) \ + if (((double)ht->size*VIR_JOB_BUCKET_LEN)/(ht->num_entries) >= \ + VIR_JOB_REHASH_THRESHOLD){ \ + jobHashRehash(ht); \ + } + + +/* Doesn't lock ht, so should only be called with a write lock held on ht*/ +static int jobHashRehash(jobHash *ht); + +/* look for id in the hash table ht and return the entry associated with it + if id isn't it the table return null*/ +static jobHashEntry* +jobLookup(const jobHash *ht, virJobID id) +{ + if (virOnce(&job_once, jobControlInit) < 0) { + return NULL; + } + READ_LOCK_TABLE(ht); + int bucket = id % ht->size; + jobHashEntry *retval = NULL, *job_entry; + if (!ht->table[bucket]) { + goto end; + } + job_entry = ht->table[bucket]; + do { + if (job_entry->id == id) { + retval = job_entry; + goto end; + } + } while ((job_entry = job_entry->next)); + + end: + UNLOCK_TABLE(ht); + return retval; +} + +/* add job the hashtable of currently existing jobs, job should + have already been initialized via virJobObjInit. + returns 0 if job is already in the hash, + returns the id of the job if it was successfully added + returns -1 on error. + */ +static int +jobHashAdd(jobHash *ht, virJobObjPtr job) +{ + if (virOnce(&job_once, jobControlInit) < 0) { + return -1; + } + virJobID id = job->id; + if (jobLookup(ht, id)) { /* check if job is in the table*/ + return 0; + } + int bucket = id % ht->size; + jobHashEntry *new_entry; + if (VIR_ALLOC_QUIET(new_entry) < 0) { + return -1; + } + jobHashEntry *last_entry = ht->table[bucket]; + *new_entry = (jobHashEntry) {.id = id, .job = job}; + + WRITE_LOCK_TABLE(ht); + if (!last_entry) { + ht->table[bucket] = new_entry; + } else { + while (last_entry->next) { + last_entry = last_entry->next; + } + last_entry->next = new_entry; + } + ht->num_entries++; + maybe_rehash(ht); + UNLOCK_TABLE(ht); + return id; +} + +/* Remove the job with the given id from the list of currently existing jobs, + this doesn't free/cleanup the actual job object, it just removes + the hash entry, this is called by virJobObjFree, so there shouldn't + be any reason to call it directly. + + return values are the same as for jobAlist add, 0 if no job found, + job id on success, -1 on some other error; +*/ +static int +jobHashRemove(jobHash *ht, virJobID id) +{ + int bucket = id % ht->size; + /*we can't just call jobLookup because we need the entry before the one + we want to free*/ + WRITE_LOCK_TABLE(ht); + jobHashEntry *entry = ht->table[bucket], *old_entry; + if (!entry) { + goto error; + } + if (entry->id != id) { + while (entry && entry->next->id != id) { + entry = entry->next; + } + } + if (!entry) { + goto error; + } + old_entry = entry->next; + entry->next = old_entry->next; + VIR_FREE(old_entry); + UNLOCK_TABLE(ht); + return id; + error: + UNLOCK_TABLE(ht); + return 0; +} +static int +jobHashRehash(jobHash *ht) +{ + size_t new_size = ht->size*VIR_JOB_GROWTH_FACTOR; + jobHashEntry **new_table; + if (VIR_ALLOC_N_QUIET(new_table, new_size)) { + return -1; + } + jobHashEntry **old_table = ht->table; + int index; + for (index = 0; index<ht->size; index++) { + jobHashEntry *old_entry = old_table[index]; + while (old_entry) { + int bucket = old_entry->id % new_size; + jobHashEntry *new_entry = new_table[bucket]; + if (!new_entry) { + new_table[bucket] = old_entry; + } else { + while (new_entry->next) { + new_entry = new_entry->next; + } + new_entry->next = old_entry; + } + old_entry = old_entry->next; + } + } + ht->size = new_size; + ht->table = new_table; + VIR_FREE(old_table); + return 1; +} + +#define jobIDFromJobInternal(job) (job->id) + +static inline virJobObjPtr +jobFromIDInternal(virJobID id) +{ + jobHashEntry *entry = jobLookup(job_hash, id); + if (entry) { + return entry->job; + } else { + return NULL; + } +} + +virJobObjPtr +virJobFromID(virJobID id) +{ + return jobFromIDInternal(id); + +} +virJobID +virJobIDFromJob(virJobObjPtr job) +{ + return jobIDFromJobInternal(job); +} + +int +virJobObjInit(virJobObjPtr job) +{ + /* This code checks to see if job was already initialized, + I don't know if this is needed or not.*/ + if (job->id != 0) { + VIR_DEBUG("job %d has already been initialized", job->id); + return 0; + } + job->id = virAtomicIntInc(&global_id); + job->maxJobsWaiting = INT_MAX; + + if (virCondInit(&job->cond) < 0) { + return -1; + } + + if (virMutexInit(&job->lock) < 0) { + virCondDestroy(&job->cond); + return -1; + } + + jobHashAdd(job_hash, job); + + return job->id; +} + +void +virJobObjFree(virJobObjPtr job) +{ + virCondDestroy(&job->cond); + jobHashRemove(job_hash, job->id); + VIR_FREE(job); +} + +void +virJobObjCleanup(virJobObjPtr job) +{ + + virCondDestroy(&job->cond); + jobHashRemove(job_hash, job->id); + memset(job, '\0', sizeof(virJobObj)); + return; +} + +int +virJobObjBegin(virJobObjPtr job, + virJobType type) +{ + LOCK_JOB(job); + if (CHECK_FLAG(job, ACTIVE)) { + VIR_DEBUG("Job %d is already running", job->id); + UNLOCK_JOB(job); + return 0; + } + VIR_DEBUG("Starting job %d with type %s", + job->id, virJobTypeToString(type)); + unsigned long long now; + if (job->id <= 0) { + goto error; /* job wasn't initialiazed*/ + } + if (virTimeMillisNow(&now) < 0) { + goto error; + } + job->type = type; + SET_FLAG(job, ACTIVE); + job->start = now; + job->owner = virThreadSelfID(); + return job->id; + error: + UNLOCK_JOB(job); + return -1; +} + +int +virJobObjEnd(virJobObjPtr job) +{ + if (job->type == VIR_JOB_NONE) { + return -1; + } + virJobObjReset(job); + virCondSignal(&job->cond); + + return job->id; +} + +/* There shouldn't be any threads waiting on job when this is called*/ +int +virJobObjReset(virJobObjPtr job) +{ + LOCK_JOB(job); + job->type = VIR_JOB_NONE; + job->flags = VIR_JOB_FLAG_NONE; + job->owner = 0; + job->jobsWaiting = 0; + UNLOCK_JOB(job); + + return job->id; +} + +int +virJobObjAbort(virJobObjPtr job) +{ + int retval; + LOCK_JOB(job); + if (CHECK_FLAG(job, ACTIVE) || CHECK_FLAG(job, SUSPENDED)) { + SET_FLAG(job, ABORTED); + retval = job->id; + } else { + retval = -1; + } + UNLOCK_JOB(job); + return retval; +} + +int +virJobObjAbortAndSignal(virJobObjPtr job) +{ + int retval; + LOCK_JOB(job); + if (CHECK_FLAG(job, ACTIVE) || CHECK_FLAG(job, SUSPENDED)) { + SET_FLAG(job, ABORTED); + retval = job->id; + virCondBroadcast(&job->cond); + } else { + retval = -1; + } + UNLOCK_JOB(job); + return retval; +} + +/* lock should be held by the calling function*/ +int +virJobObjWait(virJobObjPtr job, + virMutexPtr lock, + unsigned long long limit) +{ + + bool job_lock = false; + int retval; + if (CHECK_FLAG_ATOMIC(job, ACTIVE)) { /* if the job isn't active we're fine*/ + if (virAtomicIntInc(&job->jobsWaiting) > job->maxJobsWaiting) { + errno = 0; + goto error; + } + if (!lock) { + LOCK_JOB(job); + lock = &job->lock; + job_lock = true; + } + while (CHECK_FLAG_ATOMIC(job, ACTIVE)) { + if (limit) { + retval = virCondWaitUntil(&job->cond, lock, limit); + } else { + retval = virCondWait(&job->cond, lock); + } + if (retval < 0) { + goto error; + } + } + } + virAtomicIntDec(&job->jobsWaiting); + if (job_lock) { + UNLOCK_JOB(job); + } + return job->id; + + error: + virAtomicIntDec(&job->jobsWaiting); + if (job_lock) { + UNLOCK_JOB(job); + } + if (!errno) { + return -3; + } else if (errno == ETIMEDOUT) { + return -2; + } else { + return -1; + } +} + +int +virJobObjSuspend(virJobObjPtr job) +{ + if (!CHECK_FLAG_ATOMIC(job, ACTIVE)) { + return -1; + } + LOCK_JOB(job); + SET_FLAG(job, SUSPENDED); + UNSET_FLAG(job, ACTIVE); + UNLOCK_JOB(job); + return job->id; +} +int +virJobObjResume(virJobObjPtr job) +{ + if (!CHECK_FLAG_ATOMIC(job, SUSPENDED)) { + return -1; + } + LOCK_JOB(job); + UNSET_FLAG(job, SUSPENDED); + SET_FLAG(job, ACTIVE); + UNLOCK_JOB(job); + return job->id; +} +int +virJobObjResumeIfNotAborted(virJobObjPtr job) +{ + int retval; + LOCK_JOB(job); + if (!CHECK_FLAG(job, SUSPENDED)) { + retval = -1; + } else if (CHECK_FLAG(job, ABORTED)) { + retval = 0; + } else { + UNSET_FLAG(job, SUSPENDED); + SET_FLAG(job, ACTIVE); + retval = job->id; + } + UNLOCK_JOB(job); + return retval; +} + +void +virJobObjSetMaxWaiters(virJobObjPtr job, int max) +{ + virAtomicIntSet(&job->maxJobsWaiting, max); +} + +bool +virJobObjCheckAbort(virJobObjPtr job) +{ + return CHECK_FLAG_ATOMIC(job, ABORTED); +} +bool +virJobObjActive(virJobObjPtr job) +{ + return CHECK_FLAG_ATOMIC(job, ACTIVE); +} + +/* since we need to be able to return a negitive answer on error + the time difference we can return is only half of the maximum + possible time. This shouldn't pose any real issue. +*/ +long long +virJobObjCheckTime(virJobObjPtr job) +{ + if (!CHECK_FLAG(job, ACTIVE)) { + return 0; + } + unsigned long long now; + if (virTimeMillisNow(&now) < 0) { + return -1; + } + return now - job->start; +} + +void +virJobObjSignal(virJobObjPtr job, bool all) +{ + if (all) { + virCondBroadcast(&job->cond); + } else { + virCondSignal(&job->cond); + } +} diff --git a/src/util/virjobcontrol.h b/src/util/virjobcontrol.h new file mode 100644 index 0000000..235cc06 --- /dev/null +++ b/src/util/virjobcontrol.h @@ -0,0 +1,342 @@ +/* + * virjobcontrol.h Core implementation of job control + * + * Copyright (C) 2014 Tucker DiNapoli + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Tucker DiNapoli + */ + + +#ifndef __JOB_CONTROL_H__ +#define __JOB_CONTROL_H__ + +#include <stdlib.h> + +#include "viratomic.h" +#include "virthread.h" +#include "virutil.h" + + +/* The general type of the job, specifically if it will modify + the object it is acting on.*/ +typedef enum { + VIR_JOB_NONE = 0, /*no job running*/ + VIR_JOB_QUERY = 0x1, /* job will not change object state (read-only)*/ + VIR_JOB_MODIFY = 0x2, /* job may change object state (read-write)*/ + VIR_JOB_DESTROY = 0x4, /* job will destroy the object it is acting on */ + VIR_JOB_LAST +} virJobType; +VIR_ENUM_DECL(virJob); + + +/* General metadata/flags for jobs + + more specific flags can be added for speciic drivers, + to do this the first value of the enum for the extra flags + should be set to (VIR_JOB_FLAG_LAST << 1) and each additional + flag should be set to the last flag shifted to the left by 1. + +*/ +typedef enum { + VIR_JOB_FLAG_NONE = 0x000, /* No job is active */ + VIR_JOB_FLAG_ACTIVE = 0x001, /* Job is active */ + /* These next flags are used to indicate how progress should be measured + and are currently unused + */ + VIR_JOB_FLAG_TIME_BOUND = 0x002, /* Job is bound by a specific ammount of time */ + VIR_JOB_FLAG_MEM_BOUND = 0x004, /* Job is bound by a specific ammount of memory */ + VIR_JOB_FLAG_FILE_BOUND = 0x008, /* Job is bound by a specific number of files */ + VIR_JOB_FLAG_DATA_BOUND = 0x010, /* Job is bound by a specific ammount of data */ + VIR_JOB_FLAG_UNBOUNDED = 0x020, /* Job has no specific bound */ + /* These flags indicate a job is no longer active but still exists + */ + VIR_JOB_FLAG_SUSPENDED = 0x040, /* Job is Suspended and can be resumed */ + VIR_JOB_FLAG_COMPLETED = 0x080, /* Job has finished, but isn't cleaned up */ + VIR_JOB_FLAG_FAILED = 0x100, /* Job hit error, but isn't cleaned up */ + VIR_JOB_FLAG_ABORTED = 0x200, /* Job was aborted, but isn't cleaned up */ + VIR_JOB_FLAG_LAST = 0x400 +} virJobFlag; + + +typedef int virJobID; /*signed so negitive values can be returned on error*/ +typedef struct _virJobObj virJobObj; +typedef virJobObj *virJobObjPtr; +typedef struct _virJobInfo virJobInfo; +typedef virJobInfo *virJobInfoPtr; + +struct _virJobObj { + /* this should only be locked when changing the job object, not while running a job + it's use is mostly optional, but it is needed for waiting on a job*/ + virMutex lock;/*should have a compile time option to enable/disable locking */ + virCond cond; /* Use to coordinate jobs (is this necessary?) */ + virJobType type; /* The type of the job */ + unsigned long long owner; /* Thread id which set current job */ + unsigned long long start; /* when the job started*/ + int jobsWaiting; /* number jobs waiting on cond */ + int maxJobsWaiting; /* max number of jobs that can wait on cond */ + /* information about the job, this is a private struct, access to information + about the job should be obtained by functions */ + virJobInfoPtr info; + virJobID id; /* the job id, constant, and unique */ + virJobFlag flags; /* The current state of the job */ + void *privateData; +}; + +/* Getting information on a currently running job is currently unimplmeneted and + any suggestions on what should go into the api for querying/setting job + progress information would be appreciated +*/ +typedef enum { + VIR_JOB_INFO_NONE = 0, + VIR_JOB_INFO_TIME, + VIR_JOB_INFO_MEM, + VIR_JOB_INFO_FILE, + VIR_JOB_INFO_DATA, + VIR_JOB_INFO_LAST +} virJobInfoType; + +struct _virJobInfo { + /* these fields are for compatability with existing job + information structs. + */ + virDomainBlockJobInfoPtr blockJobInfoPtr; + virDomainJobInfoPtr domainJobInfoPtr; + + unsigned long long progressTotal; + unsigned long long dataTotal; + unsigned long long memTotal; + unsigned long long filesTotal; + unsigned long long timeTotal; + + unsigned long long progressProcessed; + unsigned long long timeProcessed; + unsigned long long memProcessed; + unsigned long long filesProcessed; + unsigned long long dataProcessed; + + unsigned long long progressRemaining; + unsigned long long dataRemaining; + unsigned long long memRemaining; + unsigned long long filesRemaining; + unsigned long long timeRemaining; +}; + +/** + * virJobObjInit: + * @job: Pointer to the job object to initialize + * + * Initialize the job object given, should be called before any other + * job function. + * + * Like all job functions it returns the job id on success and -1 on error + */ +int virJobObjInit(virJobObjPtr job); +/** + * virJobObjFree: + * @job: Pointer to the job object to free + * + * Cleanup/free all resources/memory assoicated with job + */ +void virJobObjFree(virJobObjPtr job); +/** + * virJobObjCleanup: + * @job: Pointer to the job object to cleanup + * + * Cleanup all resources assoicated with job, and zero out the + * corrsponding memory, but do not free it. + */ + +void virJobObjCleanup(virJobObjPtr job); +/** + * virJobObjBegin: + * @job: The job to begin + * @type: The type of job that is being started + * + * Marks job as active, sets the calling thread as the owner of the job, + * sets the job's start time to the current time and sets it's type to type. + * + * End job should be called after this, once the job is done + * + * Returns the job id of job on success, 0 if job is already active + * and -1 on error. + */ +int virJobObjBegin(virJobObjPtr job, + virJobType type); +/** + * virJobObjEnd: + * + * @job: The job to end + * + * Ends job, calls virJobObjReset and signals all threads waiting on job. + * + * Ending a job does not invalidate the job object, and a new job can be + * started using the same job object, call virJobObjFree or virJobObjCleanup + * in order to destroy a job object. + * + * returns the job's id on success and -1 if the job was not active. + */ +int virJobObjEnd(virJobObjPtr job); +/** + * virJobObjReset: + * + * @job: The job to reset + * + * Clears all fields of job related to running a job. This does not + * clear the job id, any configurable parameters (currently just the + * maximum number of waiting threads), or the mutex/condition variable + * assoicated with the job. This is called internally by virJobObjEnd + * and there should be few reasons to call this explicitly. + */ +int virJobObjReset(virJobObjPtr job); +/** + * virJobObjAbort: + * @job: The job to abort + * + * Marks job as aborted, since jobs are asyncronous this doesn't actually + * stop the job. The abort status of a job can be checked by + * virJobObjCheckAbort. A suspended job can be aborted. + * + * returns the job id on success and -1 if + * job is not currently running/suspended. + */ +int virJobObjAbort(virJobObjPtr job); +/** + * virJobObjAbort: + * @job: The job to abort/signal waiters + * + * Behaves identically to virJobObjAbort except all threads waiting + * on job are signaled after the abort status is set. + */ +int virJobObjAbortAndSignal(virJobObjPtr job); +/** + * virJobObjSuspend: + * @job: The job to suspend + * + * Marks job as suspended, it is up to the caller of the function + * to actually save any state assoicated with the job + * + * This function returns the job's id on success and -1 if the job + * was not active. + */ +int virJobObjSuspend(virJobObjPtr job); +/** + * virJobObjResume: + * @job The job to resume + * + * Resume job, as with virJobObjSuspend it is up to the caller to + * insure that the work being done by job is actually restarted. + * + * Since a job can be aborted while it is suspended the caller should + * check to see if job has been aborted, a convenience function + * virJobObjResumeIfNotAborted is provided. + * + * returns the job id if job was resumed and -1 if the job was not suspended. + */ +int virJobObjResume(virJobObjPtr job); + +/** + * virJobObjResumeIfNotAborted: + * @job The job to resume + * + * Behaves the same as virJobObjResume except it returns 0 and does not + * resume the job if the job was aborted while suspended. + */ +int virJobObjResumeIfNotAborted(virJobObjPtr job); + +/* returns -3 if max waiters is exceeded, -2 on timeout, -1 on other error*/ +/** + * virJobObjWait: + * @job: The job to wait on + * @lock: The lock to use in the call to virCondWait + * @limit: If not 0 the maximum ammount of time to wait (in milliseconds) + * + * This function waits for job to be completed, or to otherwise signal on it's + * condition variable. + * + * If lock is NULL the internal job lock will be used, otherwise lock should + * be held by the calling thread. + * (NOTE: I'm not sure if it's a good idea or not to use the internal lock) + * + * If limit is > 0 virCondWaitUntil is called instead of virCondWait with limit + * being used as the time parameter. + * + * If job is not currently active return successfully. + * + * Like all job functions returns the job's id on success. + * + * On Failure returns a negitive number to indicate the cause of failure + * -3 indicates the maximum number of threads were alread waiting on job + * -2 indicates that virCondWaitUntil timed out + * -1 indicates some other error in virCondWait/virCondWaitUntil + */ +int virJobObjWait(virJobObjPtr job, + virMutexPtr lock, + unsigned long long limit); +/* Should I provide a function to wait for a suspended job to resume? */ + +/** + * virJobObjSignal: + * @job: The job to signal from + * @all: If true signal all waiting threads, otherwise just signal one + * + * Signal a thread/threads waiting on job. In most cases waiting threads + * are signaled when needed internally, but this is provided if for + * some reason waiting threads need to be manually signaled. + */ + +void virJobObjSignal(virJobObjPtr job, bool all); + +/* accessor functions*/ +/** + * virJobObjCheckAbort: + * @job: The job whoes status should be checked + * + * Returns true if job has been aborted, false otherwise + */ +bool virJobObjCheckAbort(virJobObjPtr job); +/** + * virJobObjCheckTime: + * @job: The job whoes time should be checked + * + * Returns the time in milliseconds that job has been running for. + * returns 0 if job is not active and -1 if there is an error in + * getting the current time. + */ +long long virJobObjCheckTime(virJobObjPtr job); +/** + * virJobObjActive: + * @job: The job whoes status should be checked + * + * Returns true if job is currently active, false otherwise + */ +bool virJobObjActive(virJobObjPtr job); +/** + * virJobObjSetMaxWaiters: + * @job: The job to modify + * @max: The maximum number of threads to allow to wait on job at once + * + * Sets the maximum number of threads that can simultaneously wait on job. + * By default there is essentially no limit (in reality the limit is the + * maximum value that can be held by an int) + */ +void virJobObjSetMaxWaiters(virJobObjPtr job, int max); + +/* These convert between a job object and a job id. +*/ +virJobObjPtr virJobFromID(virJobID id); +virJobID virJobIDFromJob(virJobObjPtr job); +#endif -- 2.0.0 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list