Skip to content

Commit

Permalink
add thread and TLS key
Browse files Browse the repository at this point in the history
  • Loading branch information
levalup committed Jun 16, 2024
1 parent 2d6d802 commit b18a44d
Show file tree
Hide file tree
Showing 6 changed files with 322 additions and 4 deletions.
48 changes: 48 additions & 0 deletions examples/key.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// Created by Levalup.
// L.eval: Let programmer get rid of only work jobs.
//

#include <iostream>
#include <cassert>

#include "uvcxx/thread.h"
#include "uvcxx/key.h"
#include "uvcxx/utility.h"

int main() {
// create thread local storage key
// key_t is not copyable
uv::key_t tls;

uv::thread_t t1([&]() {
auto v = new int(1);

tls.set(v);

uv::sleep(1000); // wait thread-2 set value
std::cout << "Thread-1 got " << tls.ref<int>() << std::endl;

assert(tls.ref<int>() == 1);

delete v;
});
uv::thread_t t2([&]() {
auto v = new int(2);

uv::sleep(500); // wait thread-1 set value
tls.set(v);

uv::sleep(1000); // wait thread-1 get value
std::cout << "Thread-2 got " << tls.ref<int>() << std::endl;

assert(tls.ref<int>() == 2);

delete v;
});

t1.join();
t2.join();

return 0;
}
26 changes: 26 additions & 0 deletions examples/thread.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// Created by Levalup.
// L.eval: Let programmer get rid of only work jobs.
//

#include <iostream>

#include "uvcxx/thread.h"

int main() {
// create thread with work callback function
uv::thread_t t([&]() {
for (int i = 0; i < 10; ++i) {
std::cout << i << " ";
}
std::cout << std::endl;
});

// Explicit call to `join`.
// It will be implicitly called during the destruction.
t.join();
// Repeated calls are also acceptable.
t.join();

return 0;
}
4 changes: 3 additions & 1 deletion include/uvcxx.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "uvcxx/getnameinfo.h"
#include "uvcxx/handle.h"
#include "uvcxx/idle.h"
#include "uvcxx/key.h"
#include "uvcxx/lib.h"
#include "uvcxx/loop.h"
#include "uvcxx/os.h"
Expand All @@ -30,11 +31,12 @@
#include "uvcxx/signal.h"
#include "uvcxx/stream.h"
#include "uvcxx/tcp.h"
#include "uvcxx/thread.h"
#include "uvcxx/timer.h"
#include "uvcxx/tty.h"
#include "uvcxx/udp.h"
#include "uvcxx/udp_send.h"
#include "uvcxx/utilities.h"
#include "uvcxx/utility.h"
#include "uvcxx/work.h"
#include "uvcxx/write.h"

Expand Down
60 changes: 60 additions & 0 deletions include/uvcxx/key.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// Created by Levalup.
// L.eval: Let programmer get rid of only work jobs.
//

#ifndef LIBUVCXX_KEY_H
#define LIBUVCXX_KEY_H

#include <uv.h>

#include "cxx/except.h"
#include "inner/base.h"

namespace uv {
class key_t : public uvcxx::extend_raw_base_t<uv_key_t> {
public:
using self = key_t;
using supper = uvcxx::extend_raw_base_t<uv_key_t>;

key_t(const key_t &) = delete;

key_t &operator=(const key_t &) = delete;

key_t(key_t &&that) noexcept = default;

key_t &operator=(key_t &&that) noexcept = default;

key_t() {
auto err = uv_key_create(*this);
if (err < 0) throw uvcxx::errcode(err, "can not create key");
}

~key_t() {
uv_key_delete(*this);
}

void set(void *value) {
uv_key_set(*this, value);
}

[[nodiscard]]
void *get() const {
return uv_key_get(*this);
}

template<typename T>
[[nodiscard]]
T *get() const {
return (T *) uv_key_get(*this);
}

template<typename T>
[[nodiscard]]
T &ref() const {
return *((T *) uv_key_get(*this));
}
};
}

#endif //LIBUVCXX_KEY_H
174 changes: 174 additions & 0 deletions include/uvcxx/thread.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
//
// Created by Levalup.
// L.eval: Let programmer get rid of only work jobs.
//

#ifndef LIBUVCXX_THREAD_H
#define LIBUVCXX_THREAD_H

#include <functional>

#include "uv.h"

#include "uvcxx/cxx/except.h"
#include "uvcxx/cxx/version.h"
#include "uvcxx/inner/base.h"

namespace uv {
namespace inner {
class thread_t {
public:
using self = thread_t;
using raw_t = uv_thread_t;

using entry_t = std::function<void(void)>;

raw_t tid{};
std::atomic<bool> m_joined{false};

thread_t(const thread_t &) = delete;

thread_t &operator=(const thread_t &) = delete;

explicit thread_t(entry_t entry)
: m_entry(std::move(entry)) {
auto err = uv_thread_create(&tid, raw_entry, this);
if (err < 0) throw uvcxx::errcode(err);
}

#if UVCXX_SATISFY_VERSION(1, 26, 0)

explicit thread_t(entry_t entry, const uv_thread_options_t *params)
: m_entry(std::move(entry)) {
auto err = uv_thread_create_ex(&tid, params, raw_entry, this);
if (err < 0) throw uvcxx::errcode(err);
}

#endif

int join() {
bool joined = false;
if (!m_joined.compare_exchange_strong(joined, true)) { return 0; }

auto err = uv_thread_join(&tid);
if (err < 0) UVCXX_THROW_OR_RETURN(err, err);
return err;
}


private:
entry_t m_entry;

static void raw_entry(void *arg) {
try {
((self *) arg)->m_entry();
} catch (...) {
}
}
};
}

class thread_t final : public uvcxx::base_t {
public:
using self = thread_t;
using raw_t = uv_thread_t;

using inner_t = inner::thread_t;
using entery_t = inner_t::entry_t;

thread_t(const thread_t &) = delete;

thread_t &operator=(const thread_t &) = delete;

thread_t(thread_t &&that) noexcept = default;

thread_t &operator=(thread_t &&that) noexcept = default;

thread_t(std::nullptr_t) {}

explicit thread_t(entery_t entry)
: m_thread(new inner_t(std::move(entry))) {}

#if UVCXX_SATISFY_VERSION(1, 26, 0)

explicit thread_t(entery_t entry, const uv_thread_options_t *params)
: m_thread(new inner_t(std::move(entry), params)) {}

#endif

~thread_t() {
join();
delete m_thread;
}

int join() {
if (m_thread) return m_thread->join();
return 0;
}

#if UVCXX_SATISFY_VERSION(1, 45, 0)

int setaffinity(char *cpumask, char *oldmask, size_t mask_size) {
return uv_thread_setaffinity(*this, cpumask, oldmask, mask_size);
}

int getaffinity(char *cpumask, size_t mask_size) const {
return uv_thread_getaffinity(*this, cpumask, mask_size);
}

#endif

[[nodiscard]]
bool equal(const self &other) const {
return uv_thread_equal(*this, other);
}

int setpriority(int priority) {
auto err = uv_thread_setpriority(m_thread->tid, priority);
if (err < 0) UVCXX_THROW_OR_RETURN(err, err);
return err;
}

int getpriority(int *priority) const {
auto err = uv_thread_getpriority(m_thread->tid, priority);
if (err < 0) UVCXX_THROW_OR_RETURN(err, err);
return err;
}

[[nodiscard]]
int getpriority() const {
int priority = 0;
auto err = uv_thread_getpriority(m_thread->tid, &priority);
if (err < 0) UVCXX_THROW_OR_RETURN(err, 0);
return priority;
}

bool operator==(const self &other) const {
return equal(other);
}

bool operator!=(const self &other) const {
return !equal(other);
}

public:
explicit operator bool() const { return m_thread; }

operator raw_t *() { return &m_thread->tid; }

operator raw_t *() const { return &m_thread->tid; }

private:
inner::thread_t *m_thread = nullptr;
};

namespace thread {
#if UVCXX_SATISFY_VERSION(1, 45, 0)

inline int getcpu() { return uv_thread_getcpu(); }

#endif
}
}

#endif //LIBUVCXX_THREAD_H
14 changes: 11 additions & 3 deletions include/uvcxx/utilities.h → include/uvcxx/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
// L.eval: Let programmer get rid of only work jobs.
//

#ifndef LIBUVCXX_UTILITIES_H
#define LIBUVCXX_UTILITIES_H
#ifndef LIBUVCXX_UTILITY_H
#define LIBUVCXX_UTILITY_H

#include "cxx/version.h"

namespace uv {
/**
Expand All @@ -14,6 +16,12 @@ namespace uv {
* If you need to use the related interfaces, please continue to utilize the `uv_*` versions in C language.
* The implementation of `uv_random`-related interfaces can be found in `random.h`.
*/

#if UVCXX_SATISFY_VERSION(1, 34, 0)

inline void sleep(unsigned int msec) { return uv_sleep(msec); }

#endif
}

#endif //LIBUVCXX_UTILITIES_H
#endif //LIBUVCXX_UTILITY_H

0 comments on commit b18a44d

Please sign in to comment.