Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: runtime: add way to start a stoppable goroutine, and a way to stop it #25664

Closed
win-t opened this issue May 31, 2018 · 10 comments
Closed

Comments

@win-t
Copy link

win-t commented May 31, 2018

There must be a way to kill goroutine. Let say there is a function that written in C, but this function maybe contains a bug (infinite loop) or the halting problem.

Consider following code:

package main

// extern int is_human_existence_pointless();
import "C"

import (
	"fmt"
	"runtime"
	"time"
)

func is_human_existence_pointless() <-chan string {
	c := make(chan string)
	c2 := make(chan string)

	var goid runtime.Something

	go func() {
		goid = runtime.CurrentGoroutine()
		answer := int(C.is_human_existence_pointless()) != 0
		if answer {
			c <- "Yes"
		} else {
			c <- "No"
		}
	}()

	go func() {
		select {
		case <-time.After(1 * time.Hour):
			runtime.KillGoroutine(goid)
			c2 <- "Not sure"
		case ans := <-c:
			c2 <- ans
		}
	}()

	return c2
}

func main() {
	fmt.Println("is human existence pointless ?")
	fmt.Println(<-is_human_existence_pointless())
}

if is_human_existence_pointless is written on Go, we can use exit channel and select combination. but that not true on C function.

or maybe I'm do it wrong?

@gopherbot gopherbot added this to the Proposal milestone May 31, 2018
@win-t
Copy link
Author

win-t commented May 31, 2018

I'm building some system that consumes Lua as the embedded language. The user can interact with the system using Lua language (of course with some predefined interface). But right now, I have no idea how to limit the execution time (memory consumption can be limited with lua_Alloc).

@ALTree
Copy link
Member

ALTree commented May 31, 2018

The why is there no goroutine ID may be relevant here. It explains that it is a deliberate design decision. Quoting from the first paragraph:

Goroutines do not have names; they are just anonymous workers. They expose no unique identifier, name, or data structure to the programmer. Some people are surprised by this, expecting the go statement to return some item that can be used to access and control the goroutine later.

Also:

or maybe I'm do it wrong?
[...]
But right now, I have no idea how to limit the execution time

Keep in mind that we don't use the issue tracker for asking questions, it's just for actual bugs or proposals. You framed this as a Language Change Proposal, but to me it seems more like you are actually just asking how to solve a specific design issue you encountered. If that's the case, please see the Questions wiki page; it has a list of good places for asking questions.

@win-t
Copy link
Author

win-t commented May 31, 2018

Okay @ALTree, forget about my problem. The "or maybe I'm do it wrong?" part is about this proposal, maybe there is a better way other than this one. And the next comment is about the use case.

The point is, I'm proposing a way to terminate long-running C function. Because that is missing part of cgo ecosystem. Go is concurrent programming language but it also allows call C code, and most of C library doesn't handle such of concurrency pattern.

@win-t
Copy link
Author

win-t commented May 31, 2018

I 100% agree with why is there no goroutine ID, that is why I'm not 100% confident with my own proposal, that is why I need other opinion about this

@win-t
Copy link
Author

win-t commented May 31, 2018

another design, this is much better I think, because we don't expose anything about go routine

package main

// extern int is_human_existence_pointless();
import "C"

import (
	"fmt"
	"runtime"
	"time"
)

func is_human_existence_pointless() <-chan string {
	c := make(chan string)
	c2 := make(chan string)

	doneCh := runtime.KillableGoroutine(func() {
		answer := int(C.is_human_existence_pointless()) != 0
		if answer {
			c <- "Yes"
		} else {
			c <- "No"
		}
	})
	go func() {
		select {
		case <-time.After(1 * time.Hour):
			close(doneCh)
			c2 <- "Not sure"
		case ans := <-c:
			c2 <- ans
		}
	}()

	return c2
}

func main() {
	fmt.Println("is human existence pointless ?")
	fmt.Println(<-is_human_existence_pointless())
}

@ianlancetaylor
Copy link
Contributor

I don't see a proposal here. What are you suggesting be changed in the language or standard library?

@win-t
Copy link
Author

win-t commented Jun 1, 2018

Sorry, I didn't make it clear.
the proposal is to add func KillableGoroutine(f func()) chan struct{} to runtime package.

I'm still learning into the implementation of goroutine, and maybe I'm not eligible to implement it. But the idea:

func KillableGoroutine(f func()) chan struct{} {
	var thread something // AAAA
	completeCh := make(chan struct{})
	killCh := make(chan struct{})
	go func() {
		LockOSThread() // runtime package already provide this
		thread = currentOSThread() // BBBB
		f()
		close(completeCh) // CCCC
		LockOSThread() // runtime package already provide this
	}()
	go func() {
		select {
		case <-completeCh:
		case <-killCh:
			killOSThread(thread) // DDDD
		}
	}()
	return killCh
}

things that need to figure out:

  • type something ... in line with AAAA
  • func currentOSThread() something in line with BBBB
  • func killOSThread(something) in line with DDDD
  • race condition, what will happen when killCh is closed before line with CCCC is executed
  • does killing OS thread create memory leak?, what will happen to the rest of the system when we force-kill OS thread?

@ianlancetaylor ianlancetaylor changed the title proposal: there must be a way to kill goroutine proposal: runtime: add way to start a stoppable goroutine, and a way to stop it Jun 1, 2018
@ianlancetaylor
Copy link
Contributor

Are you suggesting that it should be possible to stop a goroutine at any arbitrary point while it is running? That seems complex and difficult. Historically the POSIX threads interface defined a pthread_cancel function, and it's been a source of great complexity and bugs ever since.

@randall77
Copy link
Contributor

What happens if killCh is closed before BBBB is executed?

@win-t
Copy link
Author

win-t commented Jun 1, 2018

@ianlancetaylor, I got your point.
I also think the same thing, what about resource that allocated in C is_human_existence_pointless, who is gonna clean-up them.
The only sane way I can think of to this problem is to create another process that call C is_human_existence_pointless and IPC to that process, kill the process when it takes to long, the OS will do the cleaing up.

So, I vote to discard this proposal. Feel free to close this issue.

@golang golang locked and limited conversation to collaborators Jun 1, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants