From 33ba94e784610a89779557770eb404f46b574653 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 31 May 2024 20:02:59 +0800 Subject: [PATCH] c/pthread --- _demo/thread/thd.go | 25 +++++++ c/pthread/pthread.go | 160 +++++++++++++++++++++++++++++++++++++++++++ ssa/expr.go | 60 +++++++++------- ssa/stmt_builder.go | 15 ++++ 4 files changed, 235 insertions(+), 25 deletions(-) create mode 100644 _demo/thread/thd.go create mode 100644 c/pthread/pthread.go diff --git a/_demo/thread/thd.go b/_demo/thread/thd.go new file mode 100644 index 00000000..3df27aab --- /dev/null +++ b/_demo/thread/thd.go @@ -0,0 +1,25 @@ +package main + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/pthread" +) + +var key pthread.Key + +func main() { + key.Create(nil) + key.Set(c.Pointer(c.Str("main value\n"))) + + var thd pthread.Thread + pthread.Create(&thd, nil, func(arg c.Pointer) c.Pointer { + key.Set(c.Pointer(c.Str("thread value\n"))) + c.Printf(c.Str("Hello, thread\nTLS: %s"), key.Get()) + return c.Pointer(c.Str("Back to main\n")) + }, nil) + + var retval c.Pointer + pthread.Join(thd, &retval) + + c.Printf(c.Str("%sTLS: %s"), retval, key.Get()) +} diff --git a/c/pthread/pthread.go b/c/pthread/pthread.go new file mode 100644 index 00000000..3f5a95aa --- /dev/null +++ b/c/pthread/pthread.go @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package pthread + +import ( + _ "unsafe" + + "github.com/goplus/llgo/c" +) + +const ( + LLGoPackage = "decl" +) + +func __noop__() c.Int { + return 0 +} + +// ----------------------------------------------------------------------------- + +type aThread struct { + Unused [8]byte +} + +// Thread represents a POSIX thread. +type Thread = *aThread + +// The pthread_create() function starts a new thread in the calling +// process. The new thread starts execution by invoking +// start_routine(); arg is passed as the sole argument of +// start_routine(). +// +// The new thread terminates in one of the following ways: +// +// - It calls pthread_exit(3), specifying an exit status value that +// is available to another thread in the same process that calls +// pthread_join(3). +// +// - It returns from start_routine(). This is equivalent to +// calling pthread_exit(3) with the value supplied in the return +// statement. +// +// - It is canceled (see pthread_cancel(3)). +// +// - Any of the threads in the process calls exit(3), or the main +// thread performs a return from main(). This causes the +// termination of all threads in the process. +// +// On success, pthread_create() returns 0; on error, it returns an +// error number, and the contents of *thread are undefined. +// +// See https://man7.org/linux/man-pages/man3/pthread_create.3.html +// +//go:linkname Create C.pthread_create +func Create(pthread *Thread, attr *Attr, routine func(c.Pointer) c.Pointer, arg c.Pointer) c.Int + +// The pthread_join() function waits for the thread specified by +// thread to terminate. If that thread has already terminated, then +// pthread_join() returns immediately. The thread specified by +// thread must be joinable. +// +// If retval is not NULL, then pthread_join() copies the exit status +// of the target thread (i.e., the value that the target thread +// supplied to pthread_exit(3)) into the location pointed to by +// retval. If the target thread was canceled, then PTHREAD_CANCELED +// is placed in the location pointed to by retval. +// +// If multiple threads simultaneously try to join with the same +// thread, the results are undefined. If the thread calling +// pthread_join() is canceled, then the target thread will remain +// joinable (i.e., it will not be detached). +// +// See https://man7.org/linux/man-pages/man3/pthread_join.3.html +// +//go:linkname Join C.pthread_join +func Join(thread Thread, retval *c.Pointer) c.Int + +// The pthread_exit() function terminates the calling thread and +// returns a value via retval that (if the thread is joinable) is +// available to another thread in the same process that calls +// pthread_join(3). +// +// See https://man7.org/linux/man-pages/man3/pthread_exit.3.html +// +//go:linkname Exit C.pthread_exit +func Exit(retval c.Pointer) + +// The pthread_cancel() function sends a cancelation request to the +// thread thread. +// +// See https://man7.org/linux/man-pages/man3/pthread_cancel.3.html +// +//go:linkname Cancel C.pthread_cancel +func Cancel(thread Thread) c.Int + +// ----------------------------------------------------------------------------- + +// Attr represents a POSIX thread attributes. +type Attr struct { + Detached byte + SsSp *c.Char + SsSize uintptr +} + +// llgo:link (*Attr).Init C.pthread_attr_init +func (attr *Attr) Init() c.Int { return 0 } + +// llgo:link (*Attr).Destroy C.pthread_attr_destroy +func (attr *Attr) Destroy() c.Int { return 0 } + +// llgo:link (*Attr).GetDetached C.pthread_attr_getdetachstate +func (attr *Attr) GetDetached(detached *c.Int) c.Int { return 0 } + +// llgo:link (*Attr).SetDetached C.pthread_attr_setdetachstate +func (attr *Attr) SetDetached(detached c.Int) c.Int { return 0 } + +// llgo:link (*Attr).GetStackSize C.pthread_attr_getstacksize +func (attr *Attr) GetStackSize(stackSize *uintptr) c.Int { return 0 } + +// llgo:link (*Attr).SetStackSize C.pthread_attr_setstacksize +func (attr *Attr) SetStackSize(stackSize uintptr) c.Int { return 0 } + +// llgo:link (*Attr).GetStackAddr C.pthread_attr_getstackaddr +func (attr *Attr) GetStackAddr(stackAddr *c.Pointer) c.Int { return 0 } + +// llgo:link (*Attr).SetStackAddr C.pthread_attr_setstackaddr +func (attr *Attr) SetStackAddr(stackAddr c.Pointer) c.Int { return 0 } + +// ----------------------------------------------------------------------------- +// Thread Local Storage + +type Key c.Uint + +// llgo:link (*Key).Create C.pthread_key_create +func (key *Key) Create(destructor func(c.Pointer)) c.Int { return 0 } + +// llgo:link Key.Delete C.pthread_key_delete +func (key Key) Delete() c.Int { return 0 } + +// llgo:link Key.Get C.pthread_getspecific +func (key Key) Get() c.Pointer { return nil } + +// llgo:link Key.Set C.pthread_setspecific +func (key Key) Set(value c.Pointer) c.Int { return __noop__() } + +// ----------------------------------------------------------------------------- diff --git a/ssa/expr.go b/ssa/expr.go index 9c3cfae0..fc50de9c 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -892,32 +892,8 @@ func (b Builder) InlineCall(fn Expr, args ...Expr) (ret Expr) { // t2 = println(t0, t1) // t4 = t3() func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) { - return b.Do(Call, fn, args...) -} - -type DoAction int - -const ( - Call DoAction = iota - Go - Defer -) - -// Do call a function with an action. -func (b Builder) Do(da DoAction, fn Expr, args ...Expr) (ret Expr) { if debugInstr { - var b bytes.Buffer - name := fn.impl.Name() - if name == "" { - name = "closure" - } - fmt.Fprint(&b, "Do ", da, " ", fn.kind, " ", fn.raw.Type, " ", name) - sep := ": " - for _, arg := range args { - fmt.Fprint(&b, sep, arg.impl) - sep = ", " - } - log.Println(b.String()) + logCall("Call", fn, args) } var kind = fn.kind if kind == vkPyFuncRef { @@ -948,6 +924,40 @@ func (b Builder) Do(da DoAction, fn Expr, args ...Expr) (ret Expr) { return } +func logCall(da string, fn Expr, args []Expr) { + var b bytes.Buffer + name := fn.impl.Name() + if name == "" { + name = "closure" + } + fmt.Fprint(&b, da, " ", fn.kind, " ", fn.raw.Type, " ", name) + sep := ": " + for _, arg := range args { + fmt.Fprint(&b, sep, arg.impl) + sep = ", " + } + log.Println(b.String()) +} + +type DoAction int + +const ( + Call DoAction = iota + Go + Defer +) + +// Do call a function with an action. +func (b Builder) Do(da DoAction, fn Expr, args ...Expr) (ret Expr) { + switch da { + case Call: + return b.Call(fn, args...) + case Go: + b.Go(fn, args...) + } + return +} + // The Range instruction yields an iterator over the domain and range // of X, which must be a string or map. // diff --git a/ssa/stmt_builder.go b/ssa/stmt_builder.go index 92fcd37f..7ed0a746 100644 --- a/ssa/stmt_builder.go +++ b/ssa/stmt_builder.go @@ -142,6 +142,21 @@ func (b Builder) Unreachable() { b.impl.CreateUnreachable() } +// The Go instruction creates a new goroutine and calls the specified +// function within it. +// +// Example printed form: +// +// go println(t0, t1) +// go t3() +// go invoke t5.Println(...t6) +func (b Builder) Go(fn Expr, args ...Expr) { + if debugInstr { + logCall("Go", fn, args) + } + b.Call(fn, args...) +} + // Return emits a return instruction. func (b Builder) Return(results ...Expr) { if debugInstr {