/* -*- C++ -*-

   This file declares the Weaver, Job and Thread classes.

   $ Author: Mirko Boehm $
   $ Copyright: (C) 2004, Mirko Boehm $
   $ Contact: mirko@kde.org
         http://www.kde.org
         http://www.hackerbuero.org $
   $ License: LGPL with the following explicit clarification:
         This code may be linked against any version of the Qt toolkit
         from Troll Tech, Norway. $

*/

#ifndef WEAVER_H
#define WEAVER_H

extern "C"
{
#include <stdarg.h>
#include <unistd.h>
#include <stdio.h>
}

#include <qobject.h>
#include <qptrlist.h>
#include <qthread.h>
#include <qwaitcondition.h>
#include <qmutex.h>
#include <qevent.h>

namespace KPIM {
namespace ThreadWeaver {

    /** This method prints a text message on the screen, if debugging is
        enabled. Otherwise, it does nothing. The message is thread safe,
        therefore providing that the messages appear in the order they where
        issued by the different threads.
        All messages are suppressed when Debug is false. All messages with a
        lower importance (higher number) than DebugLevel will be suppressed,
        too. Debug level 0 messages will always be printed as long as
        Debug is true.
        We use our own debugging method, since debugging threads is a more
        complicated experience than debugging single threaded
        contexts. This might change in future in the way that debug
        prints it's messages to another logging facility provided by
        the platform.
        Use setDebugLevel () to integrate adapt debug () to your platform.
    */

    extern bool Debug;
    extern int DebugLevel;

    inline void setDebugLevel (bool debug, int level)
        {
            Debug = debug;
            DebugLevel = level;
        }

    inline void debug(int severity, const char * cformat, ...)
#ifdef __GNUC__
        __attribute__ ( (format (printf, 2, 3 ) ) )
#endif
;

    inline void debug(int severity, const char * cformat, ...)
    {
        if ( Debug == true && ( severity<=DebugLevel || severity == 0) )
        {
            static QMutex mutex;
            QString text;

            mutex.lock();
            va_list ap;
            va_start( ap, cformat );
            vprintf (cformat, ap);
            va_end (ap);
            mutex.unlock();
        }
    }


    class Thread;
    class Job;

    /** A class to represent the events threads generate and send to the
        Weaver object. Examples include the start or end of the processing of a
        job. Threads create the event objects and discard them after posting
        the event, since the event receiver will assume ownership of the
        event.
        Events are associated to the sending thread and possibly to a
        processed job.

        Note: Do not create and use SPR/APR events, use Job::triggerSPR or
        Job::triggerAPR to create the requests. */

    class Event : public QCustomEvent
    {
    public:
        enum Action {
            NoAction = 0,
            Finished, /// All jobs in the queue are done.
            Suspended, /// Thread queueing halted.
            ThreadStarted,
            ThreadExiting,
            ThreadBusy,
            ThreadSuspended,
            JobStarted,
            JobFinished,
            JobSPR, /// Synchronous Process Request
            JobAPR  /// Asynchronous Process Request
        };
        Event ( Action = NoAction, Thread * = 0, Job *job = 0);
        /** Return the (custom defined) event type. */
        static const int type ();
        /** The ID of the sender thread. */
        Thread* thread () const;
        /** The associated job. */
        Job* job () const;
        /** The action. */
        Action action () const;
    private:
        Action m_action;
        Thread *m_thread;
        Job *m_job;
        static const int Type;
    };

    /** A Job is a simple abstraction of an action that is to be
        executed in a thread context.
        It is essential for the ThreadWeaver library that as a kind of
        convention, the different creators of Job objects do not touch the
        protected data members of the Job until somehow notified by the
        Job. See the SPR signal for an example.

        Jobs may emit process requests as signals. Consider process requests
        as a kind of synchronized call to the main thread.
        Process Requests are a generic means for Job derivate programmers to have
        the jobs interact with the creators (in the main thread) during
        processing time. To avoid race
        conditions and extensive locking and unlocking, the thread executing the
        job is suspended during the period needed to process the request.

        There are two kinds of process requests (we introduce abbreviations,
        also in the signal names and the code,
        only to save typing). Both are emitted by signals in the main thread:
        - Synchronous Process Requests (SPR): Synchronous requests expect that the
        complete request is performed in the slots connected to the signals. For
        example, to update a widget according to the progress of the job, a SPR
        may be used. In such cases, the Job's execution will be resumed
        immediately after the signal has been processed.
        - Asynchronous Process Requests (APR): For APRs, the job emitting the
        signal does not assume anything about the amount of time needed to
        perform the operation. Therefore, the thread is not waked after the
        signal returns. The creator has to wake to thread whenever it is
        ready by calling the wakeAPR method.

        Note: When using an APR, you better make sure to receive the signal
        with some object, otherwise the calling thread will block forever!
    */
    class Job : public QObject
    {
        Q_OBJECT
    public:
        /** Construct a Job object. */
        Job(QObject* parent=0, const char* name=0);

        /** Destructor. */
        virtual ~Job();

        /** Perform the job. The thread in which this job is executed
            is given as a parameter.
            Do not overload this method to create your own Job
            implementation, overload run(). */
        virtual void execute(Thread*);

        /** Returns true if the jobs's execute method finished. */
        virtual bool isFinished() const;

        /** Wake the thread after an APR has been processed. */
        void wakeAPR ();

        /** Process events related to this job (created by the processing
            thread or the weaver or whoever). */
        virtual void processEvent ( Event* );

    signals:
        /** This signal is emitted when a thread starts to process a job. */
        void started ();
        /** This signal is emitted when a job has been finished. */
        void done ();
        /** This signal is emitted when the job needs some operation done by
            the main thread (usually the creator of the job).
            It is important to understand that the emitting thread is
            suspended until the signal returns.
            When
            the operation requested has been performed and this signal is
            finished, the thread is automatically waked.
            What operation needs to be performed has to be negotiated between
            the two objects.
            Note: This signal is an attempt to provide job programmers with a
            generic way to interact while the job is executed. I am interested
            in feedback about it's use. */
        void SPR ();
        /** Perform an Asynchronous Process Request. See SPR and the generic
            Job documentation for a comparison. */
        void APR ();
    protected:
        /** Lock this Job's mutex. */
        void lock();
        /** Unlock this Job's mutex. */
        void unlock();
        /** The method that actually performs the job. It is called from
            execute(). This method is the one to overload it with the
            job's task. */
        virtual void run () = 0;
        /** Return the thread that executes this job.
            Returns zero of the job is not currently executed. */
        Thread *thread();
        /** Call with status = true to mark this job as done. */
        virtual void setFinished(bool status);
        /** Trigger a SPR.
            This emits a signal in the main thread indicating the necessity of
            a synchronized operation. */
        void triggerSPR ();
        /** Trigger an APR.
            This emit a signal in the main thread indicating the necessity of
            an unsynchronized operation.
            The calling thread needs to ensure to wake the thread when the
            operation is done. */
        void triggerAPR ();

        bool m_finished;

        QMutex *m_mutex;

        Thread * m_thread;

        QWaitCondition *m_wc;
    };

    class Weaver;

    /** The class Thread is used to represent the worker threads in
        the weaver's inventory. It is not meant to be overloaded. */
    class Thread : public QThread
    {
    public:
        /** Create a thread.
            These thread objects are only used inside the Weaver parent
            object. */
        Thread(Weaver *parent);

        /** The destructor. */
        ~Thread();

        /** Overloaded to execute the assigned job.
            This will NOT return until shutdown() is called. The
            thread will try to execute one job after the other, asking
            the Weaver parent for a new job when the assigned one is
            finished.
            If no jobs are available, the thread will suspend.
            After shutdown() is called, the thread will end as soon as
            the currently assigned job is done.
        */
        void run();

        /* Provide the msleep() method (protected in QThread) to be
           available  for executed jobs. */
        void msleep(unsigned long msec);

        /** Returns the thread id.
            This id marks the respective Thread object, and must
            therefore not be confused with, e.g., the pthread thread
            ID. */
        const unsigned int id() const;

        /** Post an event, will be received and processed by the Weaver. */
        void post (Event::Action, Job* = 0);

    private:
        Weaver *m_parent;

        const unsigned int m_id;

        static unsigned int sm_Id;

        static unsigned int makeId();
    };

    /** A weaver is the manager of worker threads (Thread objects) to
        which it assigns jobs from it's queue. */
    class Weaver : public QObject
    {
        Q_OBJECT
    public:
        Weaver (QObject* parent=0, const char* name=0,
                int inventoryMin = 4, // minimal number of provided threads
                int inventoryMax = 32); // maximum number of provided threads
        virtual ~Weaver ();
        /** Add a job to be executed. */
        virtual void enqueue (Job*);
        /** Enqueue all jobs in the given list.
            This is an atomic operation, no jobs will start
            before all jobs in the list are enqueued.
            If you need a couple of jobs done and want to receive the
            finished () signal afterwards, use this method to queue
            them. Otherwise, when enqueueing your jobs
            individually, there is a chance that you receive more than
            one finished signal. */
        void enqueue (QPtrList<Job> jobs);
        /** Remove a job from the queue.
            If the job qas queued but not started so far, it is simple
            removed from the queue. For now, it is unsupported to
            dequeue a job once its execution has started.
            For that case, you will have to provide a method to interrupt your
            job's execution (and receive the done signal).
            Returns true if the job has been dequeued, false if the
            job has already been started or is not found in the
            queue. */
        virtual bool dequeue (Job*);
        /** Remove all queued jobs.
            Please note that this will not kill the threads, therefore
            all jobs that are being processed will be continued. */
        virtual void dequeue ();
        /** Get notified when a thread has finished a job.
            This is done automatically. */
        // virtual void jobFinished(Thread *);
        /** Finish all queued operations, then return.
            This method is used in imperative programs that cannot react on
            events to have the controlling (main) thread wait wait for the
            jobs to finish.
            Warning: This will suspend your thread!
            Warning: If your jobs enter for example an infinite loop, this
                     will never return! */
        virtual void finish();
        /** Suspend job execution if state = true, otherwise resume
            job execution if it was suspended.
            When suspending, all threads are allowed to finish the
            currently assigned job but will not receive a new
            assignment.
            When all threads are done processing the assigned job, the
            signal suspended will() be emitted.
            If you call suspend (true) and there are no jobs left to
            be done, you will immidiately receive the suspended()
            signal. */
        virtual void suspend (bool state);
        /** Is the queue empty? */
        bool isEmpty () const;
        /** Is the weaver idle?
            The weaver is idle if no jobs are queued and no jobs are processed
            by the threads (m_active is zero). */
        bool isIdle () const;
        /** Returns the number of pending jobs. */
        int queueLength ();
        /** Assign a job to the calling thread.
            This is supposed to be called from the Thread objects in
            the inventory.
            Returns 0 if the weaver is shutting down, telling the
            calling thread to finish and exit.
            If no jobs are available and shut down is not in progress,
            the calling thread is suspended until either condition is
            met.
            In previous, threads give the job they have completed. If this is
            the first job, previous is zero. */
        virtual Job* applyForWork (Thread *thread, Job *previous);
        /** Lock the mutex for this weaver. The threads in the
            inventory need to lock the weaver's mutex to synchronize
            the job management. */
        void lock ();
        /** Unlock. See lock(). */
        void unlock ();
        /** Post an event that is handled by this object, but in the main
            (GUI) thread. Different threads may use this method to communicate
            with the main thread.
            thread and job mark the objects associated with this event. */
               void post (Event::Action, Thread* = 0, Job* = 0);
        /** Returns the current number of threads in the inventory. */
        int threads () const;
    signals:
        /** This signal is emitted when the Weaver has finished ALL currently
            queued jobs.
            If a number of jobs is enqueued sequentially, this signal might be
            emitted a couple of times (what happens is that all already queued
            jobs have been processed while you still add new ones). This is
            not a bug, but the intended behaviour. */
        void finished ();
        /** Thread queueing has been suspended.
            When suspend is called with state = true, all threads are
            allowed to finish their job. When the last thread
            finished, this signal is emitted. */
        void suspended ();
        /** This signal is emitted when a job is done. It is up to the
            programmer if this signal or the done signal of the job is more
            handy. */
        void jobDone (Job*);
// The following signals are used mainly for debugging purposes.
        void threadCreated (Thread *);
        void threadDestroyed (Thread *);
        void threadBusy (Thread *);
        void threadSuspended (Thread *);

    protected:
        /** Schedule enqueued jobs to be executed by idle threads.
            This will try to distribute as many jobs as possible
            to all idle threads. */
        void assignJobs();
        /** Check incoming events for user defined ones. The threads use user
            defined events to communicate with the Weaver. */
        bool event ( QEvent* );
        /** The thread inventory. */
        QPtrList<Thread> m_inventory;
        /** The job queue. */
        QPtrList<Job> m_assignments;
        /** The number of jobs that are assigned to the worker
            threads, but not finished. */
        int m_active;
        /** Stored setting. */
        int m_inventoryMin;
        /** Stored setting . */
        int m_inventoryMax;
        /** Wait condition all idle or done threads wait for. */
        QWaitCondition m_jobAvailable;
        /** Wait for a job to finish. */
        QWaitCondition m_jobFinished;
        /** Indicates if the weaver is shutting down and exiting it's
            threads. */
        bool m_shuttingDown;
        /** m_running is set to true when a job is enqueued and set to false
            when the job finishes that was the last in the queue.
            E.g., this will flip from false to true to false when you
            continuously enqueue one single job. */
        bool m_running;
        /** If m_suspend is true, no new jobs will be assigned to
            threads.
            Jobs may be queued, but will not be processed until suspend
            (false) is called. */
        bool m_suspend;
    private:
        /** Mutex to serialize operations. */
        QMutex *m_mutex;
    };
} // namespace ThreadWeaver
} // namespace KPIM

#endif // defined WEAVER_H
