mirror of
https://github.com/golang/go
synced 2024-11-12 09:30:25 -07:00
s/Thread/Goroutine/
R=rsc APPROVED=rsc DELTA=328 (112 added, 110 deleted, 106 changed) OCL=34356 CL=34356
This commit is contained in:
parent
920c6106ca
commit
05522cd51e
@ -54,7 +54,7 @@ type EventHook interface {
|
|||||||
|
|
||||||
type Event interface {
|
type Event interface {
|
||||||
Process() *Process;
|
Process() *Process;
|
||||||
Thread() *Thread;
|
Goroutine() *Goroutine;
|
||||||
String() string;
|
String() string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,15 +147,15 @@ func (h *commonHook) handle(e Event) (EventAction, os.Error) {
|
|||||||
type commonEvent struct {
|
type commonEvent struct {
|
||||||
// The process of this event
|
// The process of this event
|
||||||
p *Process;
|
p *Process;
|
||||||
// The thread of this event.
|
// The goroutine of this event.
|
||||||
t *Thread;
|
t *Goroutine;
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *commonEvent) Process() *Process {
|
func (e *commonEvent) Process() *Process {
|
||||||
return e.p;
|
return e.p;
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *commonEvent) Thread() *Thread {
|
func (e *commonEvent) Goroutine() *Goroutine {
|
||||||
return e.t;
|
return e.t;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,8 +187,8 @@ type breakpointHook struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A Breakpoint event occurs when a process reaches a particular
|
// A Breakpoint event occurs when a process reaches a particular
|
||||||
// program counter. When this event is handled, the current thread
|
// program counter. When this event is handled, the current goroutine
|
||||||
// will be the thread that reached the program counter.
|
// will be the goroutine that reached the program counter.
|
||||||
type Breakpoint struct {
|
type Breakpoint struct {
|
||||||
commonEvent;
|
commonEvent;
|
||||||
osThread ptrace.Thread;
|
osThread ptrace.Thread;
|
||||||
@ -234,38 +234,38 @@ func (b *Breakpoint) PC() ptrace.Word {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Breakpoint) String() string {
|
func (b *Breakpoint) String() string {
|
||||||
// TODO(austin) Include process name and thread
|
// TODO(austin) Include process name and goroutine
|
||||||
// TODO(austin) Use line:pc or at least sym+%#x
|
// TODO(austin) Use line:pc or at least sym+%#x
|
||||||
return fmt.Sprintf("breakpoint at %#x", b.pc);
|
return fmt.Sprintf("breakpoint at %#x", b.pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Thread create/exit
|
* Goroutine create/exit
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type threadCreateHook struct {
|
type goroutineCreateHook struct {
|
||||||
commonHook;
|
commonHook;
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *threadCreateHook) String() string {
|
func (h *goroutineCreateHook) String() string {
|
||||||
return "thread create";
|
return "goroutine create";
|
||||||
}
|
}
|
||||||
|
|
||||||
// A ThreadCreate event occurs when a process creates a new Go thread.
|
// A GoroutineCreate event occurs when a process creates a new
|
||||||
// When this event is handled, the current thread will be the newly
|
// goroutine. When this event is handled, the current goroutine will
|
||||||
// created thread.
|
// be the newly created goroutine.
|
||||||
type ThreadCreate struct {
|
type GoroutineCreate struct {
|
||||||
commonEvent;
|
commonEvent;
|
||||||
parent *Thread;
|
parent *Goroutine;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parent returns the thread that created this thread. May be nil if
|
// Parent returns the goroutine that created this goroutine. May be
|
||||||
// this event is the creation of the first thread.
|
// nil if this event is the creation of the first goroutine.
|
||||||
func (e *ThreadCreate) Parent() *Thread {
|
func (e *GoroutineCreate) Parent() *Goroutine {
|
||||||
return e.parent;
|
return e.parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ThreadCreate) String() string {
|
func (e *GoroutineCreate) String() string {
|
||||||
// TODO(austin) Include process name
|
// TODO(austin) Include process name
|
||||||
if e.parent == nil {
|
if e.parent == nil {
|
||||||
return fmt.Sprintf("%v created", e.t);
|
return fmt.Sprintf("%v created", e.t);
|
||||||
@ -273,22 +273,22 @@ func (e *ThreadCreate) String() string {
|
|||||||
return fmt.Sprintf("%v created by %v", e.t, e.parent);
|
return fmt.Sprintf("%v created by %v", e.t, e.parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
type threadExitHook struct {
|
type goroutineExitHook struct {
|
||||||
commonHook;
|
commonHook;
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *threadExitHook) String() string {
|
func (h *goroutineExitHook) String() string {
|
||||||
return "thread exit";
|
return "goroutine exit";
|
||||||
}
|
}
|
||||||
|
|
||||||
// A ThreadExit event occurs when a Go thread exits.
|
// A GoroutineExit event occurs when a Go goroutine exits.
|
||||||
type ThreadExit struct {
|
type GoroutineExit struct {
|
||||||
commonEvent;
|
commonEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ThreadExit) String() string {
|
func (e *GoroutineExit) String() string {
|
||||||
// TODO(austin) Include process name
|
// TODO(austin) Include process name
|
||||||
//return fmt.Sprintf("%v exited", e.t);
|
//return fmt.Sprintf("%v exited", e.t);
|
||||||
// For debugging purposes
|
// For debugging purposes
|
||||||
return fmt.Sprintf("thread %#x exited", e.t.g.addr().base);
|
return fmt.Sprintf("goroutine %#x exited", e.t.g.addr().base);
|
||||||
}
|
}
|
||||||
|
@ -10,27 +10,28 @@ import (
|
|||||||
"ptrace";
|
"ptrace";
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Thread represents a Go thread.
|
// A Goroutine represents a goroutine in a remote process.
|
||||||
type Thread struct {
|
type Goroutine struct {
|
||||||
g remoteStruct;
|
g remoteStruct;
|
||||||
frame *Frame;
|
frame *Frame;
|
||||||
dead bool;
|
dead bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thread) String() string {
|
func (t *Goroutine) String() string {
|
||||||
if t.dead {
|
if t.dead {
|
||||||
return "<dead thread>";
|
return "<dead thread>";
|
||||||
}
|
}
|
||||||
// TODO(austin) Give threads friendly ID's
|
// TODO(austin) Give threads friendly ID's, possibly including
|
||||||
|
// the name of the entry function.
|
||||||
return fmt.Sprintf("thread %#x", t.g.addr().base);
|
return fmt.Sprintf("thread %#x", t.g.addr().base);
|
||||||
}
|
}
|
||||||
|
|
||||||
// isG0 returns true if this thread if the internal idle thread
|
// isG0 returns true if this thread if the internal idle thread
|
||||||
func (t *Thread) isG0() bool {
|
func (t *Goroutine) isG0() bool {
|
||||||
return t.g.addr().base == t.g.r.p.sys.g0.addr().base;
|
return t.g.addr().base == t.g.r.p.sys.g0.addr().base;
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thread) resetFrame() {
|
func (t *Goroutine) resetFrame() {
|
||||||
// TODO(austin) NewFrame can abort
|
// TODO(austin) NewFrame can abort
|
||||||
// TODO(austin) Reuse any live part of the current frame stack
|
// TODO(austin) Reuse any live part of the current frame stack
|
||||||
// so existing references to Frame's keep working.
|
// so existing references to Frame's keep working.
|
||||||
@ -38,7 +39,7 @@ func (t *Thread) resetFrame() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Out selects the caller frame of the current frame.
|
// Out selects the caller frame of the current frame.
|
||||||
func (t *Thread) Out() os.Error {
|
func (t *Goroutine) Out() os.Error {
|
||||||
// TODO(austin) Outer can abort
|
// TODO(austin) Outer can abort
|
||||||
f := t.frame.Outer();
|
f := t.frame.Outer();
|
||||||
if f != nil {
|
if f != nil {
|
||||||
@ -48,7 +49,7 @@ func (t *Thread) Out() os.Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// In selects the frame called by the current frame.
|
// In selects the frame called by the current frame.
|
||||||
func (t *Thread) In() os.Error {
|
func (t *Goroutine) In() os.Error {
|
||||||
f := t.frame.Inner();
|
f := t.frame.Inner();
|
||||||
if f != nil {
|
if f != nil {
|
||||||
t.frame = f;
|
t.frame = f;
|
||||||
@ -69,24 +70,24 @@ func readylockedBP(ev Event) (EventAction, os.Error) {
|
|||||||
sp := regs.SP();
|
sp := regs.SP();
|
||||||
addr := sp + ptrace.Word(p.PtrSize());
|
addr := sp + ptrace.Word(p.PtrSize());
|
||||||
arg := remotePtr{remote{addr, p}, p.runtime.G};
|
arg := remotePtr{remote{addr, p}, p.runtime.G};
|
||||||
g := arg.Get();
|
gp := arg.Get();
|
||||||
if g == nil {
|
if gp == nil {
|
||||||
return EAStop, UnknownThread{b.osThread, 0};
|
return EAStop, UnknownGoroutine{b.osThread, 0};
|
||||||
}
|
}
|
||||||
gs := g.(remoteStruct);
|
gs := gp.(remoteStruct);
|
||||||
t := &Thread{gs, nil, false};
|
g := &Goroutine{gs, nil, false};
|
||||||
p.threads[gs.addr().base] = t;
|
p.goroutines[gs.addr().base] = g;
|
||||||
|
|
||||||
// Enqueue thread creation event
|
// Enqueue goroutine creation event
|
||||||
parent := b.Thread();
|
parent := b.Goroutine();
|
||||||
if parent.isG0() {
|
if parent.isG0() {
|
||||||
parent = nil;
|
parent = nil;
|
||||||
}
|
}
|
||||||
p.postEvent(&ThreadCreate{commonEvent{p, t}, parent});
|
p.postEvent(&GoroutineCreate{commonEvent{p, g}, parent});
|
||||||
|
|
||||||
// If we don't have any thread selected, select this one
|
// If we don't have any thread selected, select this one
|
||||||
if p.curThread == nil {
|
if p.curGoroutine == nil {
|
||||||
p.curThread = t;
|
p.curGoroutine = g;
|
||||||
}
|
}
|
||||||
|
|
||||||
return EADefault, nil;
|
return EADefault, nil;
|
||||||
@ -96,18 +97,18 @@ func goexitBP(ev Event) (EventAction, os.Error) {
|
|||||||
b := ev.(*Breakpoint);
|
b := ev.(*Breakpoint);
|
||||||
p := b.Process();
|
p := b.Process();
|
||||||
|
|
||||||
t := b.Thread();
|
g := b.Goroutine();
|
||||||
t.dead = true;
|
g.dead = true;
|
||||||
|
|
||||||
addr := t.g.addr().base;
|
addr := g.g.addr().base;
|
||||||
p.threads[addr] = nil, false;
|
p.goroutines[addr] = nil, false;
|
||||||
|
|
||||||
// Enqueue thread exit event
|
// Enqueue thread exit event
|
||||||
p.postEvent(&ThreadExit{commonEvent{p, t}});
|
p.postEvent(&GoroutineExit{commonEvent{p, g}});
|
||||||
|
|
||||||
// If we just exited our selected thread, selected another
|
// If we just exited our selected goroutine, selected another
|
||||||
if p.curThread == t {
|
if p.curGoroutine == g {
|
||||||
p.selectSomeThread();
|
p.selectSomeGoroutine();
|
||||||
}
|
}
|
||||||
|
|
||||||
return EADefault, nil;
|
return EADefault, nil;
|
@ -39,23 +39,24 @@ func (e ProcessNotStopped) String() string {
|
|||||||
return "process not stopped";
|
return "process not stopped";
|
||||||
}
|
}
|
||||||
|
|
||||||
// An UnknownThread error is an internal error representing an
|
// An UnknownGoroutine error is an internal error representing an
|
||||||
// unrecognized G structure pointer.
|
// unrecognized G structure pointer.
|
||||||
type UnknownThread struct {
|
type UnknownGoroutine struct {
|
||||||
OSThread ptrace.Thread;
|
OSThread ptrace.Thread;
|
||||||
GoThread ptrace.Word;
|
Goroutine ptrace.Word;
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e UnknownThread) String() string {
|
func (e UnknownGoroutine) String() string {
|
||||||
return fmt.Sprintf("internal error: unknown thread (G %#x)", e.GoThread);
|
return fmt.Sprintf("internal error: unknown goroutine (G %#x)", e.Goroutine);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A NoCurrentThread error occurs when no thread is currently selected
|
// A NoCurrentGoroutine error occurs when no goroutine is currently
|
||||||
// in a process (or when there are no threads in a process).
|
// selected in a process (or when there are no goroutines in a
|
||||||
type NoCurrentThread struct {}
|
// process).
|
||||||
|
type NoCurrentGoroutine struct {}
|
||||||
|
|
||||||
func (e NoCurrentThread) String() string {
|
func (e NoCurrentGoroutine) String() string {
|
||||||
return "no current thread";
|
return "no current goroutine";
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Process represents a remote attached process.
|
// A Process represents a remote attached process.
|
||||||
@ -92,14 +93,14 @@ type Process struct {
|
|||||||
|
|
||||||
// Event hooks
|
// Event hooks
|
||||||
breakpointHooks map[ptrace.Word] *breakpointHook;
|
breakpointHooks map[ptrace.Word] *breakpointHook;
|
||||||
threadCreateHook *threadCreateHook;
|
goroutineCreateHook *goroutineCreateHook;
|
||||||
threadExitHook *threadExitHook;
|
goroutineExitHook *goroutineExitHook;
|
||||||
|
|
||||||
// Current thread, or nil if there are no threads
|
// Current goroutine, or nil if there are no goroutines
|
||||||
curThread *Thread;
|
curGoroutine *Goroutine;
|
||||||
|
|
||||||
// Threads by the address of their G structure
|
// Goroutines by the address of their G structure
|
||||||
threads map[ptrace.Word] *Thread;
|
goroutines map[ptrace.Word] *Goroutine;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -115,9 +116,9 @@ func NewProcess(proc ptrace.Process, arch Arch, syms *sym.GoSymTable) (*Process,
|
|||||||
syms: syms,
|
syms: syms,
|
||||||
types: make(map[ptrace.Word] *remoteType),
|
types: make(map[ptrace.Word] *remoteType),
|
||||||
breakpointHooks: make(map[ptrace.Word] *breakpointHook),
|
breakpointHooks: make(map[ptrace.Word] *breakpointHook),
|
||||||
threadCreateHook: new(threadCreateHook),
|
goroutineCreateHook: new(goroutineCreateHook),
|
||||||
threadExitHook: new(threadExitHook),
|
goroutineExitHook: new(goroutineExitHook),
|
||||||
threads: make(map[ptrace.Word] *Thread),
|
goroutines: make(map[ptrace.Word] *Goroutine),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fill in remote runtime
|
// Fill in remote runtime
|
||||||
@ -134,18 +135,18 @@ func NewProcess(proc ptrace.Process, arch Arch, syms *sym.GoSymTable) (*Process,
|
|||||||
return nil, FormatError("failed to find runtime symbol 'sys.goexit'");
|
return nil, FormatError("failed to find runtime symbol 'sys.goexit'");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get current threads
|
// Get current goroutines
|
||||||
p.threads[p.sys.g0.addr().base] = &Thread{p.sys.g0, nil, false};
|
p.goroutines[p.sys.g0.addr().base] = &Goroutine{p.sys.g0, nil, false};
|
||||||
g := p.sys.allg.Get();
|
g := p.sys.allg.Get();
|
||||||
for g != nil {
|
for g != nil {
|
||||||
gs := g.(remoteStruct);
|
gs := g.(remoteStruct);
|
||||||
fmt.Printf("*** Found thread at %#x\n", gs.addr().base);
|
fmt.Printf("*** Found goroutine at %#x\n", gs.addr().base);
|
||||||
p.threads[gs.addr().base] = &Thread{gs, nil, false};
|
p.goroutines[gs.addr().base] = &Goroutine{gs, nil, false};
|
||||||
g = gs.Field(p.f.G.Alllink).(remotePtr).Get();
|
g = gs.Field(p.f.G.Alllink).(remotePtr).Get();
|
||||||
}
|
}
|
||||||
p.selectSomeThread();
|
p.selectSomeGoroutine();
|
||||||
|
|
||||||
// Create internal breakpoints to catch new and exited threads
|
// Create internal breakpoints to catch new and exited goroutines
|
||||||
p.OnBreakpoint(ptrace.Word(p.sys.newprocreadylocked.Entry())).(*breakpointHook).addHandler(readylockedBP, true);
|
p.OnBreakpoint(ptrace.Word(p.sys.newprocreadylocked.Entry())).(*breakpointHook).addHandler(readylockedBP, true);
|
||||||
p.OnBreakpoint(ptrace.Word(p.sys.goexit.Entry())).(*breakpointHook).addHandler(goexitBP, true);
|
p.OnBreakpoint(ptrace.Word(p.sys.goexit.Entry())).(*breakpointHook).addHandler(goexitBP, true);
|
||||||
|
|
||||||
@ -233,13 +234,13 @@ func (p *Process) bootstrap() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Process) selectSomeThread() {
|
func (p *Process) selectSomeGoroutine() {
|
||||||
// Once we have friendly thread ID's, there might be a more
|
// Once we have friendly goroutine ID's, there might be a more
|
||||||
// reasonable behavior for this.
|
// reasonable behavior for this.
|
||||||
p.curThread = nil;
|
p.curGoroutine = nil;
|
||||||
for _, t := range p.threads {
|
for _, t := range p.goroutines {
|
||||||
if !t.isG0() {
|
if !t.isG0() {
|
||||||
p.curThread = t;
|
p.curGoroutine = t;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -299,26 +300,26 @@ func (p *Process) OnBreakpoint(pc ptrace.Word) EventHook {
|
|||||||
return &breakpointHook{commonHook{nil, 0}, p, pc};
|
return &breakpointHook{commonHook{nil, 0}, p, pc};
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnThreadCreate returns the hook that is run when a Go thread is created.
|
// OnGoroutineCreate returns the hook that is run when a goroutine is created.
|
||||||
func (p *Process) OnThreadCreate() EventHook {
|
func (p *Process) OnGoroutineCreate() EventHook {
|
||||||
return p.threadCreateHook;
|
return p.goroutineCreateHook;
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnThreadExit returns the hook
|
// OnGoroutineExit returns the hook that is run when a goroutine exits.
|
||||||
func (p *Process) OnThreadExit() EventHook {
|
func (p *Process) OnGoroutineExit() EventHook {
|
||||||
return p.threadExitHook;
|
return p.goroutineExitHook;
|
||||||
}
|
}
|
||||||
|
|
||||||
// osThreadToThread looks up the Go thread running on an OS thread.
|
// osThreadToGoroutine looks up the goroutine running on an OS thread.
|
||||||
func (p *Process) osThreadToThread(t ptrace.Thread) (*Thread, os.Error) {
|
func (p *Process) osThreadToGoroutine(t ptrace.Thread) (*Goroutine, os.Error) {
|
||||||
regs, err := t.Regs();
|
regs, err := t.Regs();
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err;
|
return nil, err;
|
||||||
}
|
}
|
||||||
g := p.G(regs);
|
g := p.G(regs);
|
||||||
gt, ok := p.threads[g];
|
gt, ok := p.goroutines[g];
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, UnknownThread{t, g};
|
return nil, UnknownGoroutine{t, g};
|
||||||
}
|
}
|
||||||
return gt, nil;
|
return gt, nil;
|
||||||
}
|
}
|
||||||
@ -347,7 +348,7 @@ func (p *Process) causesToEvents() ([]Event, os.Error) {
|
|||||||
if c, err := t.Stopped(); err == nil {
|
if c, err := t.Stopped(); err == nil {
|
||||||
switch c := c.(type) {
|
switch c := c.(type) {
|
||||||
case ptrace.Breakpoint:
|
case ptrace.Breakpoint:
|
||||||
gt, err := p.osThreadToThread(t);
|
gt, err := p.osThreadToGoroutine(t);
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err;
|
return nil, err;
|
||||||
}
|
}
|
||||||
@ -416,15 +417,15 @@ func (p *Process) processEvent(ev Event) (EventAction, os.Error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
p.curThread = ev.Thread();
|
p.curGoroutine = ev.Goroutine();
|
||||||
action, err = hook.handle(ev);
|
action, err = hook.handle(ev);
|
||||||
|
|
||||||
case *ThreadCreate:
|
case *GoroutineCreate:
|
||||||
p.curThread = ev.Thread();
|
p.curGoroutine = ev.Goroutine();
|
||||||
action, err = p.threadCreateHook.handle(ev);
|
action, err = p.goroutineCreateHook.handle(ev);
|
||||||
|
|
||||||
case *ThreadExit:
|
case *GoroutineExit:
|
||||||
action, err = p.threadExitHook.handle(ev);
|
action, err = p.goroutineExitHook.handle(ev);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log.Crashf("Unknown event type %T in queue", p.event);
|
log.Crashf("Unknown event type %T in queue", p.event);
|
||||||
@ -480,7 +481,7 @@ func (p *Process) ContWait() os.Error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
for _, t := range p.threads {
|
for _, t := range p.goroutines {
|
||||||
t.resetFrame();
|
t.resetFrame();
|
||||||
}
|
}
|
||||||
p.pending, err = p.causesToEvents();
|
p.pending, err = p.causesToEvents();
|
||||||
@ -493,16 +494,16 @@ func (p *Process) ContWait() os.Error {
|
|||||||
|
|
||||||
// Out selects the caller frame of the current frame.
|
// Out selects the caller frame of the current frame.
|
||||||
func (p *Process) Out() os.Error {
|
func (p *Process) Out() os.Error {
|
||||||
if p.curThread == nil {
|
if p.curGoroutine == nil {
|
||||||
return NoCurrentThread{};
|
return NoCurrentGoroutine{};
|
||||||
}
|
}
|
||||||
return p.curThread.Out();
|
return p.curGoroutine.Out();
|
||||||
}
|
}
|
||||||
|
|
||||||
// In selects the frame called by the current frame.
|
// In selects the frame called by the current frame.
|
||||||
func (p *Process) In() os.Error {
|
func (p *Process) In() os.Error {
|
||||||
if p.curThread == nil {
|
if p.curGoroutine == nil {
|
||||||
return NoCurrentThread{};
|
return NoCurrentGoroutine{};
|
||||||
}
|
}
|
||||||
return p.curThread.In();
|
return p.curGoroutine.In();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user