~sircmpwn/hare-users

5 3

I'm confusing right now: why it work?

Details
Message ID
<CADC8Pp7Y1x4xT--0_FBpanMo08e5Js2y_NCoiE7M4cn_Tur8cw@mail.gmail.com>
DKIM signature
pass
Download raw message
I was working on `pthread` stuff before, below's the bindings I wrote
(a long time ago) and it works for a while:

```hare
export def PTHREAD_CREATE_JOINABLE: int = 0;
export def PTHREAD_CREATE_DETACHED: int = 1;

export type pthread_attr_t = nullable *opaque;

export @symbol("pthread_attr_init") fn pthread_attr_init(attr:
*pthread_attr_t) int;
```

And the following sample code works:

- All `pthread_xxx` calls return `0` meaning no error.
- The `detach_state` is also equal to `1`, so I assume that the
`pthread_attr_setdetachstate` call is correct.

Just ignore the code except `pthread_attr_init(&thread_attr)`!

```hare
let thread_attr: pthread_attr_t = null;

//
// init detachable thread attribute
//
if (pthread_attr_init(&thread_attr) != 0) return FailedToInitAttribute;

if (pthread_attr_setdetachstate(
    &thread_attr,
    PTHREAD_CREATE_DETACHED) != 0)
    return FailedToSetDetachableAttribute;

let detach_state: int = -1;
if (pthread_attr_getdetachstate(&thread_attr, &detach_state) != 0 ||
    detach_state != 1)
    return FailedToCreateDetachableThread;
```


But I started to get confused after having a look at the MUSL libc
source code, as the above hare SHOULD NOT work (but it works somehow?)
:)

In MUSL C, here is the actual type of `pthread_attr_t` (x84_64) and
`pthread_attr_init` source code:

```c
struct {
    union {
        int __i[14];
        unsigned long __s[7];
    } __u;
} pthread_attr_t;


#include "pthread_impl.h"

int pthread_attr_init(pthread_attr_t *a)
{
    *a = (pthread_attr_t){0};
    return 0;
}
```

`*a = (pthread_attr_t){0};` - dereference the given parameter and
assign the zeroed `pthread_attr_t` struct back. The size of
`pthread_attr_t` is `56` bytes (on my machine):

```c
#include <pthread.h>

// output `>>> size of pthread_attr_t: 56`
printf("\n>>> size of pthread_attr_t: %lu", sizeof(pthread_attr_t));
```


But my hare version of `pthread_attr_t` is a `nullable pointer to an
opaque type` (a `void *` that allows it to be `NULL`).

And why does this work???

```hare
let thread_attr: pthread_attr_t = null;
pthread_attr_init(&thread_attr)
```

Pass a `* *opaque` to it (that's `null` value btw), even the
`thread_attr` local var occupied 8 bytes, but `*a =
(pthread_attr_t){0};` write 56 bytes back still work? Is it supposed
to be the segmentation fault?
Details
Message ID
<D6XF3YSAUE1K.8EWEB18T9ZI5@cmpwn.com>
In-Reply-To
<CADC8Pp7Y1x4xT--0_FBpanMo08e5Js2y_NCoiE7M4cn_Tur8cw@mail.gmail.com> (view parent)
DKIM signature
pass
Download raw message
Solid "dunno" here. pthreads are definitely not supported by Hare
upstream, and whatever level of "works" you're able to get (or unable to
get) is up to you.

My best guess is that the rest of the pthread attr is left on the bottom
of your stack and you never have enough nested call frames to overwrite
it later on (or you do, and the results are not obvious).
Details
Message ID
<D6XJ0570G631.NAV3U2MKBAS5@turminal.net>
In-Reply-To
<CADC8Pp7Y1x4xT--0_FBpanMo08e5Js2y_NCoiE7M4cn_Tur8cw@mail.gmail.com> (view parent)
Sender timestamp
1736427791
DKIM signature
pass
Download raw message
> But my hare version of `pthread_attr_t` is a `nullable pointer to an
> opaque type` (a `void *` that allows it to be `NULL`).
>
> And why does this work???
>
> ```hare
> let thread_attr: pthread_attr_t = null;
> pthread_attr_init(&thread_attr)
> ```
>
> Pass a `* *opaque` to it (that's `null` value btw), even the
> `thread_attr` local var occupied 8 bytes, but `*a =
> (pthread_attr_t){0};` write 56 bytes back still work? Is it supposed
> to be the segmentation fault?

The value of thread_attr is null, but you are not passing the value, but the
address of the variable. So in thread_attr_init, its value (the null you are
talking about) is just overwritten.

Because the thread_attr object is smaller than what pthread_attr_init expects
(8 bytes instead of 56), it also writes 48 bytes past the end of it, which is
technically illegal like you say, but it isn't guaranteed to cause actual
problems, and it looks like it hasn't caused problems (yet) in your case.
But you should change pthread_attr_t to reflect it's actual type as defined in
the C library.
Details
Message ID
<CADC8Pp7WuYE-rHB4E=FWtiUx2iambMjtVSLLo=Z=F5_wSNq48g@mail.gmail.com>
In-Reply-To
<D6XJ0570G631.NAV3U2MKBAS5@turminal.net> (view parent)
Sender timestamp
1736503125
DKIM signature
pass
Download raw message
Thanks for the quick reply from you guys ("Drew DeVault" and "Bor
Grošelj Simić"), very appreciated. So, I end up with the following
considerations/solutions and questions.

1. Write the C wrapper functions to init the `pthread_xxx` struct
instance in the right way and return a pointer on the heap, something
like this:

```c
#include <pthread.h>

pthread_attr_t *init_pthread_attr(void) {
    return malloc(sizeof(pthread_attr_t));
}
```

So, on the hare side, it only needs to deal with `nullable *opaque`
and should work for all OSs I want to support. This is an advantage
because `<pthread.h>` uses a forward declaration, which conceals the
actual struct type. Different operating system implementations can
have different actual types.

For example, my hare pthread bindings `translate` from FreeBSD,
FreeBSD <pthread.h> make all `pthread_XXXX_t` become pointers

```c
/*
 * Forward structure definitions.
 *
 * These are mostly opaque to the user.
 */
struct pthread_attr;

typedef struct pthread_attr *pthread_attr_t;
```

That's why hare bindings for pthread (using `nullabe *opaque`) work
very well in FreeBSD.


But in Alpine (MUSL), `pthread_attr_t` is an actual struct type:

```c
struct { union { int __i[sizeof(long)==8?14:9]; volatile int
__vi[sizeof(long)==8?14:9]; unsigned long __s[sizeof(long)==8?7:9]; }
__u; } pthread_attr_t;
```

That said, you can't do something like this:

```hare
export type pthread_attr_t = opaque;

export @symbol("pthread_attr_init") fn pthread_attr_init(attr:
*pthread_attr_t) int;

let thread_attr: pthread_attr_t;
```

`let thread_attr: pthread_attr_t;` - This won't work, as the `opaque`
type is size unknown. You can't create it on the stack, or
`alloc/rt::malloc` on the heap, it just doesn't work.

So, by creating the C wrapper functions and compiling as `xxxx.o`
(object file) and putting it into the hare module, hare code doesn't
need to know the pthread actual struct type, and it should work (I
believe):)



2. If `1.` works and I still have time left outside my project, I will
consider reading through all "pthread" implements from OS
implementations and translating the actual struct type into Hare
struct. So I can write hare code like this:

```hare
let thread_attr  = pthread_attr_t {...};

// Or

let thread_attr: *pthread_attr_t  = alloc(pthread_attr_t {...});
```

FreeBSD: https://github.com/lattera/freebsd/blob/master/lib/libthr/thread/thr_private.h#L239
MUSL: https://github.com/kraj/musl/blob/kraj/master/include/alltypes.h.in#L85


But my question is, am I able to do something like this?

```bash
+freebsd.ha     // I know this works:)
+linux-musl.ha // Does hare support this?
+linux-gnu.ha  // Does hare support this?
```



3. I found a really weird thing, I really want to know why???

I tested on FreeBSD (without the pthread bindings issue), and I found that:

A) If I use pthread Mutex or ReadWriteLock in different threads, it
works well. `pthread_mutex_lock/pthread_mutex_unlock` and
`pthread_rwlock_rdlock/pthread_rwlock_rwlock/pthread_rwlock_unlock`
all work as excepted.

B) If I use pthread Mutex or ReadWriteLock in different threads WITH
`unix::poll` (poll a set of fds to reduce unnecessary `io::read`),
then Mutex seems always lock/unlock by the thread that runs `poll()`,
and ReadLock lock/unlock always work in the thread that runs `poll()`
(any other threads CAN'T acquire the WriteLock, it blocks forever)

C) Same with "B)" but without using `unix:poll`, instead use
`time::sleep()`, it sometimes produces the same issues with "B)"


On Alpine MUSL, the same `A) B) C)` above plus sometimes segmentation
fault (related to my wrong bindings)



4. Questions about `hare-ev`

I've already considered trying `hare-ev` (event loop), but I doubt
that `for (ev::dispatch(&loop, -1)) void;` still occupies a thread and
I have to use another thread if I want to do something else? (Can I
create different `loop` instances for different `for loop` purposes?)

Here is my requirement, plz let me know does `hare-ev` fits and how I
should use it:)

- I have a TCP server + `unix::poll` to serve a lot of TCP clients
(some of them are a single program on MCUs)
- I have a separate thread to handle timers (`a timer per tcp client`
to kill fake connections over 3G/4G and kick clients that are not
sending the same binary protocol), still working on it.
`stdlib/linux/timerfd` only support Linux so far, so I try to
implement my own to cover FreeBSD and Linux
- One more separate thread for something else task, NOT IO related
(sorting shared list/array stuff), or how to wrap it into `io::file`
then epoll can help to reduce a thread?

Would `hare-ev` fit? If so, any guide or highlight overview would be
very appreciated. BTW, Performance is NOT a high priority, as that's
the network-bound solution, not the CPU-bound solution. thanks:)

On Fri, Jan 10, 2025 at 12:47 AM Bor Grošelj Simić <bgs@turminal.net> wrote:
>
> > But my hare version of `pthread_attr_t` is a `nullable pointer to an
> > opaque type` (a `void *` that allows it to be `NULL`).
> >
> > And why does this work???
> >
> > ```hare
> > let thread_attr: pthread_attr_t = null;
> > pthread_attr_init(&thread_attr)
> > ```
> >
> > Pass a `* *opaque` to it (that's `null` value btw), even the
> > `thread_attr` local var occupied 8 bytes, but `*a =
> > (pthread_attr_t){0};` write 56 bytes back still work? Is it supposed
> > to be the segmentation fault?
>
> The value of thread_attr is null, but you are not passing the value, but the
> address of the variable. So in thread_attr_init, its value (the null you are
> talking about) is just overwritten.
>
> Because the thread_attr object is smaller than what pthread_attr_init expects
> (8 bytes instead of 56), it also writes 48 bytes past the end of it, which is
> technically illegal like you say, but it isn't guaranteed to cause actual
> problems, and it looks like it hasn't caused problems (yet) in your case.
> But you should change pthread_attr_t to reflect it's actual type as defined in
> the C library.
Details
Message ID
<D6Y00XG3I8PI.4K6PLRVENHY8@turminal.net>
In-Reply-To
<CADC8Pp7WuYE-rHB4E=FWtiUx2iambMjtVSLLo=Z=F5_wSNq48g@mail.gmail.com> (view parent)
Sender timestamp
1736475811
DKIM signature
pass
Download raw message
> Thanks for the quick reply from you guys ("Drew DeVault" and "Bor
> Grošelj Simić"), very appreciated. So, I end up with the following
> considerations/solutions and questions.
>
> 1. Write the C wrapper functions to init the `pthread_xxx` struct
> instance in the right way and return a pointer on the heap, something
> like this:
>
> ```c
> #include <pthread.h>
>
> pthread_attr_t *init_pthread_attr(void) {
>     return malloc(sizeof(pthread_attr_t));
> }
> ```
>
> So, on the hare side, it only needs to deal with `nullable *opaque`
> and should work for all OSs I want to support. This is an advantage
> because `<pthread.h>` uses a forward declaration, which conceals the
> actual struct type. Different operating system implementations can
> have different actual types.
>
> For example, my hare pthread bindings `translate` from FreeBSD,
> FreeBSD <pthread.h> make all `pthread_XXXX_t` become pointers
>
> ```c
> /*
>  * Forward structure definitions.
>  *
>  * These are mostly opaque to the user.
>  */
> struct pthread_attr;
>
> typedef struct pthread_attr *pthread_attr_t;
> ```
>
> That's why hare bindings for pthread (using `nullabe *opaque`) work
> very well in FreeBSD.
>
>
> But in Alpine (MUSL), `pthread_attr_t` is an actual struct type:
>
> ```c
> struct { union { int __i[sizeof(long)==8?14:9]; volatile int
> __vi[sizeof(long)==8?14:9]; unsigned long __s[sizeof(long)==8?7:9]; }
> __u; } pthread_attr_t;
> ```
>
> That said, you can't do something like this:
>
> ```hare
> export type pthread_attr_t = opaque;
>
> export @symbol("pthread_attr_init") fn pthread_attr_init(attr:
> *pthread_attr_t) int;
>
> let thread_attr: pthread_attr_t;
> ```
>
> `let thread_attr: pthread_attr_t;` - This won't work, as the `opaque`
> type is size unknown. You can't create it on the stack, or
> `alloc/rt::malloc` on the heap, it just doesn't work.
>
> So, by creating the C wrapper functions and compiling as `xxxx.o`
> (object file) and putting it into the hare module, hare code doesn't
> need to know the pthread actual struct type, and it should work (I
> believe):)

This will work probably. But it is not a good way of covering different
pthread implementations. I suggest you start your work with one platform, the
one that you use and know the most, and make things work there. You don't have
to deal with the actual contents what pthread_attr_t, the Hare type just needs
to match the size and the alignment of the C type that you are copying, but you
can also copy the whole definition.

Then after you have something working on your preferred platform, you can start
thinking about supporting others.


>
> 2. If `1.` works and I still have time left outside my project, I will
> consider reading through all "pthread" implements from OS
> implementations and translating the actual struct type into Hare
> struct. So I can write hare code like this:
>
> ```hare
> let thread_attr  = pthread_attr_t {...};
>
> // Or
>
> let thread_attr: *pthread_attr_t  = alloc(pthread_attr_t {...});
> ```
>
> FreeBSD: https://github.com/lattera/freebsd/blob/master/lib/libthr/thread/thr_private.h#L239
> MUSL: https://github.com/kraj/musl/blob/kraj/master/include/alltypes.h.in#L85
>
>
> But my question is, am I able to do something like this?
>
> ```bash
> +freebsd.ha     // I know this works:)
> +linux-musl.ha // Does hare support this?
> +linux-gnu.ha  // Does hare support this?
> ```

The tags are user defined, you can make up your own tags and use them in
filenames.

The right way to name them would probably be +linux+libc+musl.ha, and
+linux+libc+gnu(or glibc).ha, making the name composed of three separate tags.

To build this on musl for example, you should then add -T+libc and -T+musl to
the hare build command.

> 3. I found a really weird thing, I really want to know why???
>
> I tested on FreeBSD (without the pthread bindings issue), and I found that:
>
> A) If I use pthread Mutex or ReadWriteLock in different threads, it
> works well. `pthread_mutex_lock/pthread_mutex_unlock` and
> `pthread_rwlock_rdlock/pthread_rwlock_rwlock/pthread_rwlock_unlock`
> all work as excepted.
>
> B) If I use pthread Mutex or ReadWriteLock in different threads WITH
> `unix::poll` (poll a set of fds to reduce unnecessary `io::read`),
> then Mutex seems always lock/unlock by the thread that runs `poll()`,
> and ReadLock lock/unlock always work in the thread that runs `poll()`
> (any other threads CAN'T acquire the WriteLock, it blocks forever)
>
> C) Same with "B)" but without using `unix:poll`, instead use
> `time::sleep()`, it sometimes produces the same issues with "B)"

I'm afraid I'm unable to help you here. In general, *you should not expect
the functions from the stdlib to work in multithreaded programs* because most of
them were not designed with that use case in mind. We are slowly moving towards
improving that, but so far, doing that is not a good idea. This may be
related to mentioned behavior. Now, if you really want to use parts of the
stdlib with pthreads, you should triple check the functions that you are using
to make sure they are safe for multithreading, but *I really do not recommend
that*. It's much simpler to wait until this problem in the stdlib is resolved,
or to not use the Hare stdlib in the same program as pthreads at all.

If you are curious about this particular case, try replicating the behavior in
plain C and see what happens. If the behaviour is the same, you are likely
having some issues with the way you are using the locks. Otherwise, it's likely
that the problem you describe is related to Hare's implementation of unix::poll
and time::sleep.

> 4. Questions about `hare-ev`
>
> I've already considered trying `hare-ev` (event loop), but I doubt
> that `for (ev::dispatch(&loop, -1)) void;` still occupies a thread and
> I have to use another thread if I want to do something else? (Can I
> create different `loop` instances for different `for loop` purposes?)
>
> Here is my requirement, plz let me know does `hare-ev` fits and how I
> should use it:)
>
> - I have a TCP server + `unix::poll` to serve a lot of TCP clients
> (some of them are a single program on MCUs)
> - I have a separate thread to handle timers (`a timer per tcp client`
> to kill fake connections over 3G/4G and kick clients that are not
> sending the same binary protocol), still working on it.
> `stdlib/linux/timerfd` only support Linux so far, so I try to
> implement my own to cover FreeBSD and Linux
> - One more separate thread for something else task, NOT IO related
> (sorting shared list/array stuff), or how to wrap it into `io::file`
> then epoll can help to reduce a thread?
>
> Would `hare-ev` fit? If so, any guide or highlight overview would be
> very appreciated. BTW, Performance is NOT a high priority, as that's
> the network-bound solution, not the CPU-bound solution. thanks:)

I'm no specialist in any of the related areas, but as long as what you are
doing is IO-bound and you don't have very strict latency requirements, hare-ev
should work for you. Part of the trick is not using ev::dispatch with -1
timeout argument. Specifying timeout -1 means that the ev::dispatch will wait
for the next event before returning, but instead, you probably want a timeout
of 0, or a small positive amount. This way, the ev::dispatch call will return
after the specified timeout if it does not have any events, and you can do some
other work, then call ev::dispatch with a small positive timeout again, do some
more work, and repeat all of that. You need to take care to not spend too much
time between consecutive calls of ev::dispatch, to ensure events don't wait too
long to be processed.

I don't think there are any guides written for hare-ev yet, but there are some
examples in the repository:
https://git.sr.ht/~sircmpwn/hare-ev/tree/master/item/cmd
I'm afraid they all use -1 as the timeout parameter though.

Good luck!
Details
Message ID
<CADC8Pp6V5XNYry3Wo-c+u_0ghYcHLSrh2D9LLOGE6ENgRW586Q@mail.gmail.com>
In-Reply-To
<D6Y00XG3I8PI.4K6PLRVENHY8@turminal.net> (view parent)
Sender timestamp
1736604214
DKIM signature
pass
Download raw message
Thanks for all the detailed replies, very appreciated:)

I'm porting my previous production code from C to hare (for learning
and testing purposes BEFORE migrating from C to Hare), so pthread in C
works fine without any problem (I mean on FreeBSD, didn't test it on
Linux yet), so I doubt that the 'weird behaviors' related to Linux
(specific to Alpine Linux) or Hare impl. But of course, I will try on
another distro to confirm later:)

Btw, `C wrapper function to create pthread object on the heap and use
in hare function (binding)` works (tested). But consider what you said
before (stdlib not designed for multithreading) and the previous `cpp
issues` ...that's also a real thing I need to think about.

Our server and my personal working environment both are FreeBSD (we
have linux teammates as well) and I found that Hare experience in
FreeBSD .... it's really disappointing (the `cpp issues` I mentioned
in my previous posts). Actually, I realized that it ONLY happens when
Hare program works with `libc` or links to third-party C libraries,
pure Hare code works fine! That's why I have to use Linux in `bhyve
VM` in my FreeBSD right now (not saying Linux is bad, just personal
preference:). That's also my motivation to give it a try on `hare-ev`,
if it works, then no `libc` no `pthread` I think (I saw
statically-link in `hare-ev` example).


Ok, my next questions:

1. You mentioned this:
> You don't have to deal with the actual contents what pthread_attr_t, the Hare type just needs to match the size and the alignment of the C type that you are copying,

What that means exactly? Is that possible to give me an example to
guide me to understand?:) Also, I remember that I compared the same
struct between C and Hare, they have different byte size, then I guess
`harec`  (or `qbe`) will reorder fields for a given Hare struct to
cause different padding with C?:)


2. You also mentioned this:
> In general, *you should not expect the functions from the stdlib to work in multithreaded programs* because most of them were not designed with that use case in mind. It's much simpler to wait until this problem in the stdlib is resolved, or to not use the Hare stdlib in the same program as pthreads at all.

Yup, I will keep that in mind. I read stdlib source code sometimes, as
NO LSP currently, also good for learning. Usually the function I
called can track back to `rt::xxxx`.

But I'm wondering, are you talking about the specific I/O part? or any
module that has the `export let XXXX:YYYY = ZZZ;`?


3. I've already spent some time to read the `hare-ev` source code,
here are my questions related it:

3-A. So based on my requirement, my TCP SERVER base program should
look like this? (didn't run it at all, just imagine in editor)

```hare
def EVENT_LOOP_TIMEOUT: size = time::MILLISECOND * 50;

export fn main() void = {
    //
    // Setup TCP server loop settings (grab from the `cmd` example)
    //
    const sock = ev::listen_tcp(&loop, ip::LOCAL_V4, 12345)!;
    // ...ignore the related code
    log::println("Listening on 127.0.0.1:12345");

    //
    // Main forever loop
    //
    for (true) {
        //
        // Handle async socket IOs, break if it returns `false` (means
`stopped`)
        //
        if (!ev::dispatch(&loop, EVENT_LOOP_TIMEOUT)!) break;

        //
        // Process another tasks (my original thread's logic)
        //
        // ...
    };
    os::exit(state.exit);
};
```


3-B. Also, because `ev` runs all callbacks one-by-one in the main
thread, that said no data race needs to be worried about, right?:) (I
believe so)


3-C. You mentioned this:

>You need to take care to not spend too much time between consecutive calls of ev::dispatch, to ensure events don't wait too long to be processed.

What exactly does `too long` mean?:)
In production, we serve a lot tcp clients on per node (starts from
thousands), that said it would have a events peak case of all clients
try to reconnect when a server has been restarted, that would be the
case to push a lot of (read) events into `loop.events` and then handle
them one-by-one, I started to doubt that this would be real-case
problem for me....


3-D. It seems it only supports `+linux` so far via `EPOLL`, no
existing support for `BSDs`? Or any plan to support in the future?:)

On Fri, Jan 10, 2025 at 2:07 PM Bor Grošelj Simić <bgs@turminal.net> wrote:
>
> > Thanks for the quick reply from you guys ("Drew DeVault" and "Bor
> > Grošelj Simić"), very appreciated. So, I end up with the following
> > considerations/solutions and questions.
> >
> > 1. Write the C wrapper functions to init the `pthread_xxx` struct
> > instance in the right way and return a pointer on the heap, something
> > like this:
> >
> > ```c
> > #include <pthread.h>
> >
> > pthread_attr_t *init_pthread_attr(void) {
> >     return malloc(sizeof(pthread_attr_t));
> > }
> > ```
> >
> > So, on the hare side, it only needs to deal with `nullable *opaque`
> > and should work for all OSs I want to support. This is an advantage
> > because `<pthread.h>` uses a forward declaration, which conceals the
> > actual struct type. Different operating system implementations can
> > have different actual types.
> >
> > For example, my hare pthread bindings `translate` from FreeBSD,
> > FreeBSD <pthread.h> make all `pthread_XXXX_t` become pointers
> >
> > ```c
> > /*
> >  * Forward structure definitions.
> >  *
> >  * These are mostly opaque to the user.
> >  */
> > struct pthread_attr;
> >
> > typedef struct pthread_attr *pthread_attr_t;
> > ```
> >
> > That's why hare bindings for pthread (using `nullabe *opaque`) work
> > very well in FreeBSD.
> >
> >
> > But in Alpine (MUSL), `pthread_attr_t` is an actual struct type:
> >
> > ```c
> > struct { union { int __i[sizeof(long)==8?14:9]; volatile int
> > __vi[sizeof(long)==8?14:9]; unsigned long __s[sizeof(long)==8?7:9]; }
> > __u; } pthread_attr_t;
> > ```
> >
> > That said, you can't do something like this:
> >
> > ```hare
> > export type pthread_attr_t = opaque;
> >
> > export @symbol("pthread_attr_init") fn pthread_attr_init(attr:
> > *pthread_attr_t) int;
> >
> > let thread_attr: pthread_attr_t;
> > ```
> >
> > `let thread_attr: pthread_attr_t;` - This won't work, as the `opaque`
> > type is size unknown. You can't create it on the stack, or
> > `alloc/rt::malloc` on the heap, it just doesn't work.
> >
> > So, by creating the C wrapper functions and compiling as `xxxx.o`
> > (object file) and putting it into the hare module, hare code doesn't
> > need to know the pthread actual struct type, and it should work (I
> > believe):)
>
> This will work probably. But it is not a good way of covering different
> pthread implementations. I suggest you start your work with one platform, the
> one that you use and know the most, and make things work there. You don't have
> to deal with the actual contents what pthread_attr_t, the Hare type just needs
> to match the size and the alignment of the C type that you are copying, but you
> can also copy the whole definition.
>
> Then after you have something working on your preferred platform, you can start
> thinking about supporting others.
>
>
> >
> > 2. If `1.` works and I still have time left outside my project, I will
> > consider reading through all "pthread" implements from OS
> > implementations and translating the actual struct type into Hare
> > struct. So I can write hare code like this:
> >
> > ```hare
> > let thread_attr  = pthread_attr_t {...};
> >
> > // Or
> >
> > let thread_attr: *pthread_attr_t  = alloc(pthread_attr_t {...});
> > ```
> >
> > FreeBSD: https://github.com/lattera/freebsd/blob/master/lib/libthr/thread/thr_private.h#L239
> > MUSL: https://github.com/kraj/musl/blob/kraj/master/include/alltypes.h.in#L85
> >
> >
> > But my question is, am I able to do something like this?
> >
> > ```bash
> > +freebsd.ha     // I know this works:)
> > +linux-musl.ha // Does hare support this?
> > +linux-gnu.ha  // Does hare support this?
> > ```
>
> The tags are user defined, you can make up your own tags and use them in
> filenames.
>
> The right way to name them would probably be +linux+libc+musl.ha, and
> +linux+libc+gnu(or glibc).ha, making the name composed of three separate tags.
>
> To build this on musl for example, you should then add -T+libc and -T+musl to
> the hare build command.
>
> > 3. I found a really weird thing, I really want to know why???
> >
> > I tested on FreeBSD (without the pthread bindings issue), and I found that:
> >
> > A) If I use pthread Mutex or ReadWriteLock in different threads, it
> > works well. `pthread_mutex_lock/pthread_mutex_unlock` and
> > `pthread_rwlock_rdlock/pthread_rwlock_rwlock/pthread_rwlock_unlock`
> > all work as excepted.
> >
> > B) If I use pthread Mutex or ReadWriteLock in different threads WITH
> > `unix::poll` (poll a set of fds to reduce unnecessary `io::read`),
> > then Mutex seems always lock/unlock by the thread that runs `poll()`,
> > and ReadLock lock/unlock always work in the thread that runs `poll()`
> > (any other threads CAN'T acquire the WriteLock, it blocks forever)
> >
> > C) Same with "B)" but without using `unix:poll`, instead use
> > `time::sleep()`, it sometimes produces the same issues with "B)"
>
> I'm afraid I'm unable to help you here. In general, *you should not expect
> the functions from the stdlib to work in multithreaded programs* because most of
> them were not designed with that use case in mind. We are slowly moving towards
> improving that, but so far, doing that is not a good idea. This may be
> related to mentioned behavior. Now, if you really want to use parts of the
> stdlib with pthreads, you should triple check the functions that you are using
> to make sure they are safe for multithreading, but *I really do not recommend
> that*. It's much simpler to wait until this problem in the stdlib is resolved,
> or to not use the Hare stdlib in the same program as pthreads at all.
>
> If you are curious about this particular case, try replicating the behavior in
> plain C and see what happens. If the behaviour is the same, you are likely
> having some issues with the way you are using the locks. Otherwise, it's likely
> that the problem you describe is related to Hare's implementation of unix::poll
> and time::sleep.
>
> > 4. Questions about `hare-ev`
> >
> > I've already considered trying `hare-ev` (event loop), but I doubt
> > that `for (ev::dispatch(&loop, -1)) void;` still occupies a thread and
> > I have to use another thread if I want to do something else? (Can I
> > create different `loop` instances for different `for loop` purposes?)
> >
> > Here is my requirement, plz let me know does `hare-ev` fits and how I
> > should use it:)
> >
> > - I have a TCP server + `unix::poll` to serve a lot of TCP clients
> > (some of them are a single program on MCUs)
> > - I have a separate thread to handle timers (`a timer per tcp client`
> > to kill fake connections over 3G/4G and kick clients that are not
> > sending the same binary protocol), still working on it.
> > `stdlib/linux/timerfd` only support Linux so far, so I try to
> > implement my own to cover FreeBSD and Linux
> > - One more separate thread for something else task, NOT IO related
> > (sorting shared list/array stuff), or how to wrap it into `io::file`
> > then epoll can help to reduce a thread?
> >
> > Would `hare-ev` fit? If so, any guide or highlight overview would be
> > very appreciated. BTW, Performance is NOT a high priority, as that's
> > the network-bound solution, not the CPU-bound solution. thanks:)
>
> I'm no specialist in any of the related areas, but as long as what you are
> doing is IO-bound and you don't have very strict latency requirements, hare-ev
> should work for you. Part of the trick is not using ev::dispatch with -1
> timeout argument. Specifying timeout -1 means that the ev::dispatch will wait
> for the next event before returning, but instead, you probably want a timeout
> of 0, or a small positive amount. This way, the ev::dispatch call will return
> after the specified timeout if it does not have any events, and you can do some
> other work, then call ev::dispatch with a small positive timeout again, do some
> more work, and repeat all of that. You need to take care to not spend too much
> time between consecutive calls of ev::dispatch, to ensure events don't wait too
> long to be processed.
>
> I don't think there are any guides written for hare-ev yet, but there are some
> examples in the repository:
> https://git.sr.ht/~sircmpwn/hare-ev/tree/master/item/cmd
> I'm afraid they all use -1 as the timeout parameter though.
>
> Good luck!
Reply to thread Export thread (mbox)