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

go/common: Make DefaultSink thread-safe #12485

Merged
merged 1 commit into from Mar 24, 2023
Merged

go/common: Make DefaultSink thread-safe #12485

merged 1 commit into from Mar 24, 2023

Conversation

abhinav
Copy link
Contributor

@abhinav abhinav commented Mar 23, 2023

diag.DefaultSink is not safe for concurrent use.
Whether we use it with os.File or with a bytes.Buffer,
both targets do not synchronize their writes,
so this leaves room for interleaving of messages.
Making diag.DefaultSink thread-safe is generally desirable,
but is also a requirement for #12438
so that it can run upgrades concurrently
and print warnings as tasks fail.

This makes the result of DefaultSink thread-safe
by adding a mutex around the targeted stdout and stderr.

Note:
As a special case, when stdout and stderr are the same
we want to use the same mutex for them.
This matches the behavior of os/exec.Cmd

// If Stdout and Stderr are the same writer, and have a type that can
// be compared with ==, at most one goroutine at a time will call Write.
Stdout io.Writer
Stderr io.Writer

A writer implementation can be anything,
and is not guaranteed to be comparable with ==.
When that happens, the == will panic.
To prevent regressions, we have to handle that case
and treat uncomparable writers as different.
This is also similar to how os/exec does it:
https://cs.opensource.google/go/go/+/refs/tags/go1.20.2:src/os/exec/exec.go;l=475

Copy link
Contributor Author

abhinav commented Mar 23, 2023

@pulumi-bot
Copy link
Contributor

pulumi-bot commented Mar 23, 2023

Changelog

[uncommitted] (2023-03-24)

Bug Fixes

  • [sdk] Make default logger thread-safe.
    #12485

diag.DefaultSink is not safe for concurrent use.
Whether we use it with os.File or with a bytes.Buffer,
both targets do not synchronize their writes,
so this leaves room for interleaving of messages.
Making diag.DefaultSink thread-safe is generally desirable,
but is also a requirement for #12438
so that it can run upgrades concurrently
and print warnings as tasks fail.

This makes the result of DefaultSink thread-safe
by adding a mutex around the targeted stdout and stderr.

Note:
As a special case, when stdout and stderr are the same
we want to use the same mutex for them.
This matches the behavior of [os/exec.Cmd][1]

    // If Stdout and Stderr are the same writer, and have a type that can
    // be compared with ==, at most one goroutine at a time will call Write.
    Stdout io.Writer
    Stderr io.Writer

  [1]: https://pkg.go.dev/os/exec#Cmd

A writer implementation can be anything,
and is not guaranteed to be comparable with `==`.
When that happens, the `==` will panic.
To prevent regressions, we have to handle that case
and treat uncomparable writers as different.
This is also similar to how os/exec does it:
https://cs.opensource.google/go/go/+/refs/tags/go1.20.2:src/os/exec/exec.go;l=475
@abhinav
Copy link
Contributor Author

abhinav commented Mar 24, 2023

bors merge

bors bot added a commit that referenced this pull request Mar 24, 2023
12485: go/common: Make DefaultSink thread-safe r=abhinav a=abhinav

diag.DefaultSink is not safe for concurrent use.
Whether we use it with os.File or with a bytes.Buffer,
both targets do not synchronize their writes,
so this leaves room for interleaving of messages.
Making diag.DefaultSink thread-safe is generally desirable,
but is also a requirement for #12438
so that it can run upgrades concurrently
and print warnings as tasks fail.

This makes the result of DefaultSink thread-safe
by adding a mutex around the targeted stdout and stderr.

Note:
As a special case, when stdout and stderr are the same
we want to use the same mutex for them.
This matches the behavior of [os/exec.Cmd][1]

    // If Stdout and Stderr are the same writer, and have a type that can
    // be compared with ==, at most one goroutine at a time will call Write.
    Stdout io.Writer
    Stderr io.Writer

  [1]: https://pkg.go.dev/os/exec#Cmd

A writer implementation can be anything,
and is not guaranteed to be comparable with `==`.
When that happens, the `==` will panic.
To prevent regressions, we have to handle that case
and treat uncomparable writers as different.
This is also similar to how os/exec does it:
https://cs.opensource.google/go/go/+/refs/tags/go1.20.2:src/os/exec/exec.go;l=475


Co-authored-by: Abhinav Gupta <abhinav@pulumi.com>
@bors
Copy link
Contributor

bors bot commented Mar 24, 2023

Build failed:

@abhinav
Copy link
Contributor Author

abhinav commented Mar 24, 2023

image

@abhinav
Copy link
Contributor Author

abhinav commented Mar 24, 2023

bors retry

bors bot added a commit that referenced this pull request Mar 24, 2023
12485: go/common: Make DefaultSink thread-safe r=abhinav a=abhinav

diag.DefaultSink is not safe for concurrent use.
Whether we use it with os.File or with a bytes.Buffer,
both targets do not synchronize their writes,
so this leaves room for interleaving of messages.
Making diag.DefaultSink thread-safe is generally desirable,
but is also a requirement for #12438
so that it can run upgrades concurrently
and print warnings as tasks fail.

This makes the result of DefaultSink thread-safe
by adding a mutex around the targeted stdout and stderr.

Note:
As a special case, when stdout and stderr are the same
we want to use the same mutex for them.
This matches the behavior of [os/exec.Cmd][1]

    // If Stdout and Stderr are the same writer, and have a type that can
    // be compared with ==, at most one goroutine at a time will call Write.
    Stdout io.Writer
    Stderr io.Writer

  [1]: https://pkg.go.dev/os/exec#Cmd

A writer implementation can be anything,
and is not guaranteed to be comparable with `==`.
When that happens, the `==` will panic.
To prevent regressions, we have to handle that case
and treat uncomparable writers as different.
This is also similar to how os/exec does it:
https://cs.opensource.google/go/go/+/refs/tags/go1.20.2:src/os/exec/exec.go;l=475


Co-authored-by: Abhinav Gupta <abhinav@pulumi.com>
@bors
Copy link
Contributor

bors bot commented Mar 24, 2023

Build failed:

@abhinav
Copy link
Contributor Author

abhinav commented Mar 24, 2023

Again
image

@abhinav
Copy link
Contributor Author

abhinav commented Mar 24, 2023

bors retry

@bors
Copy link
Contributor

bors bot commented Mar 24, 2023

Build succeeded:

@bors bors bot merged commit b73488a into master Mar 24, 2023
41 of 46 checks passed
@bors bors bot deleted the abhinav/diag-sink-race branch March 24, 2023 18:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants