/*
 * DEBUG: section 93  ICAP (RFC 3507) Client
 */

#include "squid.h"
#include "cbdata.h"
#include "TextException.h"
#include "AsyncJob.h"


AsyncJob *AsyncJob::AsyncStart(AsyncJob *job) {
    assert(job);
    job = cbdataReference(job); // unlocked when done() in callEnd()
    AsyncCall(93,5, job, AsyncJob::noteStart);
    return job;
}

AsyncJob::AsyncJob(const char *aTypeName): typeName(aTypeName), inCall(NULL)
{
}

AsyncJob::~AsyncJob()
{
}

void AsyncJob::noteStart()
{
    AsyncCallEnter(noteStart);

    start();

    AsyncCallExit();
}

void AsyncJob::start()
{
    Must(cbdataReferenceValid(this)); // locked in AsyncStart
}

void AsyncJob::mustStop(const char *aReason)
{
    Must(inCall); // otherwise nobody will delete us if we are done()
    Must(aReason);
    if (!stopReason) {
        stopReason = aReason;
        debugs(93, 5, typeName << " will stop, reason: " << stopReason);
    } else {
        debugs(93, 5, typeName << " will stop, another reason: " << aReason);
    }
}

bool AsyncJob::done() const
{
    // stopReason, set in mustStop(), overwrites all other conditions
    return stopReason != NULL || doneAll();
}

bool AsyncJob::doneAll() const
{
    return true; // so that it is safe for kids to use
}

bool AsyncJob::callStart(const char *method)
{
    debugs(93, 5, typeName << "::" << method << " called" << status());

    if (inCall) {
        // this may happen when we have bugs or when arguably buggy
        // comm interface calls us while we are closing the connection
        debugs(93, 5, HERE << typeName << "::" << inCall <<
               " is in progress; " << typeName << "::" << method <<
               " cancels reentry.");
        return false;
    }

    inCall = method;
    return true;
}

void AsyncJob::callException(const TextException &e)
{
    debugs(93, 3, typeName << "::" << inCall << " caught an exception: " <<
           e.message << ' ' << status());

    mustStop("exception");
}

void AsyncJob::callEnd()
{
    if (done()) {
        debugs(93, 5, typeName << "::" << inCall << " ends job " <<
            status());

        const char *inCallSaved = inCall;
        const char *typeNameSaved = typeName;
        void *thisSaved = this;

        swanSong();

        void *cbdata = this;
        delete this; // this is the only place where the object is deleted
        cbdataReferenceDone(cbdata); // locked by AsyncStart

        // careful: this object does not exist any more
        debugs(93, 6, HERE << typeNameSaved << "::" << inCallSaved <<
            " ended " << thisSaved);
        return;
    }

    debugs(93, 6, typeName << "::" << inCall << " ended" << status());
    inCall = NULL;
}

