You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I noticed that when user calls SetLogger/SetLoggerFunc to provide custom log handler, or configures standard logger to use custom output writer, it may happen that our C threads invoke arbitrary complex user's go code.
I feel that invoking random user's go code on random C threads may be dangerous, at best it can confuse go scheduler and probably cause performance issues, though I don't have specific examples on top of my head.
Anyway, I just decoupled this threads: C callback now writes messages to a channel, and user's go code is invoked in a separate goroutine which reads messages from that channel. Now we can be sure that user's callback stay in "go world" and callbacks invoked on C thread is small and minimal and is completely controlled by us.
slightly related stuff:
Need to fix comments for LogInfo and LogTrace levels.
// LogError enables informational messages and above.LogInfoLogLevel=2...// LogDebug enables extra verbose logging, which may hurt performance// and should not be used in production.LogTraceLogLevel=4
I feel that invoking random user's go code on random C threads may be dangerous, at best it can confuse go scheduler and probably cause performance issues, though I don't have specific examples on top of my head.
So I dug into it a bit more.
It works as follows:
C thread invokes Go entry point
Go attaches temporary M to the C thread, switches to M's stack, and invokes LockOSThread (pin goroutine to system thread)
Go invokes our callback
our callback is executed on the C thread and Go stack; it's allowed to block; it's not allowed to switch goroutines; when it blocks, instead of scheduling another goroutine, it's OS thread is suspended on a futex until woke up from other Go threads;
Conclusion:
my feeling was right, it's not OK to invoke arbitrary user code in logger callback, because executing code in a goroutine after LockOSThread may have performance penalties and user may not expect that their code will run in such environment
luckily, it's OK to block in Go callback on C thread, so a blocking write to a channel that I'm doing in this PR is acceptable; the C thread will just sleep, as intended
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains two commits:
More details about the second commit:
I noticed that when user calls SetLogger/SetLoggerFunc to provide custom log handler, or configures standard logger to use custom output writer, it may happen that our C threads invoke arbitrary complex user's go code.
I feel that invoking random user's go code on random C threads may be dangerous, at best it can confuse go scheduler and probably cause performance issues, though I don't have specific examples on top of my head.
Anyway, I just decoupled this threads: C callback now writes messages to a channel, and user's go code is invoked in a separate goroutine which reads messages from that channel. Now we can be sure that user's callback stay in "go world" and callbacks invoked on C thread is small and minimal and is completely controlled by us.