Boost logo

Boost :

From: William E. Kempf (wekempf_at_[hidden])
Date: 2003-01-08 15:03:01


I've been working on the next release of Boost.Threads and would like to discuss the current design for the thread class. Here's a quick (and rough) description of the design. Note that much of this design is based on POSIX, for those familiar with it.

class thread_cancel
{
public:
    thread_cancel();
    ~thread_cancel();
};

#if defined(BOOST_THREAD_PRIORITY_SCHEDULING)

struct sched_param
{
    int priority;
    // The rest of this structure is implementation defined
};

enum { sched_fifo, sched_rr, sched_other };
enum { scope_process, scope_system };

#endif // BOOST_THREAD_PRIORITY_SCHEDULING

class BOOST_THREAD_DECL thread : private noncopyable
{
public:
        class BOOST_THREAD_DECL attributes
        {
        public:
                attributes();
                ~attributes();

#if defined(BOOST_THREAD_ATTRIBUTES_STACKSIZE)
                attributes& stack_size(size_t size);
                size_t stack_size() const;
#endif // BOOST_THREAD_ATTRIBUTES_STACKSIZE

#if defined(BOOST_THREAD_ATTRIBUTES_STACKADDR)
                attributes& stack_address(void* addr);
                void* stack_address() const;
#endif // BOOST_THREAD_ATTRIBUTES_STACKADDR

#if defined(BOOST_THREAD_PRIORITY_SCHEDULING)
                attributes& inherit_scheduling(bool inherit);
                bool inherit_scheduling() const;
                attributes& scheduling_parameter(const sched_param& param);
                sched_param scheduling_parameter() const;
                attributes& scheduling_policy(int policy);
                int scheduling_policy() const;
                attributes& scope(int scope);
                int scope() const;
#endif // BOOST_THREAD_PRIORITY_SCHEDULING
        };

    thread();
    template <typename F>
        explicit thread(const F& threadfunc);
    template <typename F>
        thread(const F& threadfunc, attributes attr);
    ~thread();

    bool operator==(const thread& other) const;
    bool operator!=(const thread& other) const;

    void join();
    void cancel();

#if defined(BOOST_THREAD_PRIORITY_SCHEDULING)
        void set_scheduling_parameter(int policy, const sched_param& param);
        void get_scheduling_parameter(int& policy, sched_param& param) const;

        static int max_priority(int policy);
        static int min_priority(int policy);
#endif // BOOST_THREAD_PRIORITY_SCHEDULING

    static void test_cancel();
    static void sleep(const xtime& xt);
    static void yield();
};

Several portions of this are conditionally compiled based upon the capabilities of the platform, with the following #defines used:

* BOOST_THREAD_PRIORITY_SCHEDULING if the platform allows scheduling parameters to be set for a thread, such as priority.

* BOOST_THREAD_ATTRIBUTES_STACKSIZE if the platform allows the stack size for new threads to be set by the user.

* BOOST_THREAD_ATTRIBUTES_STACKADDR if the platform allows the stack address for new threads to be set by the user.

class thread_cancel is a simple type thrown by cancellation points when a thread has been requested to be cancelled.

struct sched_param is used to "portably" set the scheduling priority and other scheduling attributes. The only required member is "priority", used to specify a thread's priority. This design was borrowed from POSIX, and in fact the implementation uses the POSIX sched_param structure when BOOST_HAS_PTHREAD is defined.

The sched_fifo, sched_rr, sched_other, scope_process, and scope_system values are implementation defined, and on POSIX correspond to SCHED_FIFO, SCHED_RR, SCHED_OTHER, PTHREAD_SCOPE_PROCESS and PTHREAD_SCOPE_SYSTEM respectively.

thread::attributes::stack_size(size_t size) sets the size of the stack that will be created for a new thread constructed with this attribute object. If the implementation can't handle the specified size, std::invalid_argument is thrown.

thread::attribute::stack_address(void* addr) sets the stack address that will be used for a new thread constructed with this attribute object.

thread::attribute::inherit_scheduling(bool inherit) sets a flag indicating whether or not a thread created from this attribute object will inherit the current thread's scheduling attributes or will use the attributes found in this attribute object.

thread::attribute::scheduling_parameter(const sched_param& param) sets the scheduling parameter used when creating a thread with this attribute object. The members and valid values of sched_param used depend on the policy set (see below). If invalid or unsupported values are set for any members std::invalid_argument is thrown.

thread::attribute::scheduling_policy(int policy) sets the scheduling policy to be used. An implementation need not support the three documented policies (and in fact, may support a policy not documented, though generally this is what sched_other is for), and if an invalid or unsupported policy is specified std::invalid_argument is thrown.

thread::attributes::scope(int scope) is used to set the scope of any thread created with this attribute object. If an invalid or unsupported scope is specified a std::invalid_argument is thrown.

thread::thread() constructs a thread object that references the current thread of execution. If the current thread was not created by Boost.Thread's and resources can not be acquired by Boost.Threads for the current thread, thread_resource_error is thrown.

template <typename F> thread::thread(const F& threadfunc) constructs a thread object that references a new thread of execution which executes threadfunc(). If any resources can not be allocated to do this, thread_resource_error is thrown.

template <typename F> thread::thread(const F& threadfunc, attribute attr) constructs a thread object that references a new thread of execution created with the attributes specified in attr which executes threadfunc(). If any resources can not be allocated to do this, thread_resource_error is thrown.

thread::~thread() reduces the ref-count on the referenced thread, and if the count is 0 (indicating no other thread objects reference the thread, and the thread has run to completion) cleans up all resources.

thread::operator==() and thread::operator!=() are used to compare thread object's to determine if they reference the same thread of execution.

thread::join() waits for a thread of execution to complete, and may be called multiple times with subsequent calls returning immediately.

thread::cancel() indicates that the thread of execution should throw thread_cancel at the next call to a "cancellation point".

thread::set_scheduling_parameter() sets the scheduling policy and parameters for the referenced thread of execution. If an invalid or unsupported policy is specified, or if an invalid or unsupported value for any sched_param members is specified then a std::invalid_argument is thrown.

thread::min_priority() and thread::max_priority() return the minimum and maximum values that may be set for a thread for a given policy, and is platform specific.

thread::test_cancel() is a "cancellation point" and will throw thread_cancel if thread::cancel() has been called on the current thread of execution.

thread::sleep() and thread::yield() have not changed, except they are now considered "cancellation points" and may throw thread_cancel.

I'd appreciate comments on the above design. Specifically I have these questions:

* Are there concerns about using conditional compilation and optional portions of the library, as POSIX does? I believe this is the only way Boost.Threads and the C++ standard will be able to provide "portable" threading libraries that don't restrict implementation to a least common denominator approach.

* Are there issues with throwing std::invalid_argument for both invalid and unsupported values? Should I define Boost.Threads specific exceptions instead, seperating out the two exception types?

* Should I not nest the thread::attributes class and instead have a boost::thread_attributes class?

Beyond this, I'd appreciate any other feedback as well.

William E. Kempf
wekempf_at_[hidden]


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk