-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: cannot lazily link against GLS accessors (#127)
In order to be able to lazily link against GLS accessors, these need to be declared as variables instead of functions. This allows creation of bindings on the other side that can be conditionally satisffied by the existence of initialized declarations somewhere else. Adds an integration test to demonstrate it is possible to build programs with and without the runtime modification using this technique. Enables: DataDog/dd-trace-go#2761 --------- Signed-off-by: Eliott Bouhana <eliott.bouhana@datadoghq.com> Signed-off-by: github-actions on behalf of eliottness <github-actions@github.com> Co-authored-by: Eliott Bouhana <eliott.bouhana@datadoghq.com> Co-authored-by: github-actions on behalf of eliottness <github-actions@github.com>
- Loading branch information
1 parent
66b7af5
commit 7d606d2
Showing
5 changed files
with
185 additions
and
4 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// Unless explicitly stated otherwise all files in this repository are licensed | ||
// under the Apache License Version 2.0. | ||
// This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
// Copyright 2023-present Datadog, Inc. | ||
|
||
package gls | ||
|
||
/* | ||
extern void cgoCallback(); | ||
*/ | ||
import "C" | ||
|
||
import ( | ||
_ "runtime" // Provides go:linkname targets (if Orchestrion modifies) | ||
_ "unsafe" // For go:linkname | ||
) | ||
|
||
var ( | ||
//go:linkname __dd_orchestrion_gls_get __dd_orchestrion_gls_get | ||
__dd_orchestrion_gls_get func() any | ||
|
||
//go:linkname __dd_orchestrion_gls_set __dd_orchestrion_gls_set | ||
__dd_orchestrion_gls_set func(any) | ||
|
||
get = func() any { return nil } | ||
set = func(any) {} | ||
) | ||
|
||
func init() { | ||
if __dd_orchestrion_gls_get != nil { | ||
get = __dd_orchestrion_gls_get | ||
} | ||
if __dd_orchestrion_gls_set != nil { | ||
set = __dd_orchestrion_gls_set | ||
} | ||
} | ||
|
||
//export cgoCallback | ||
func cgoCallback() { | ||
set("I am inside a cgo callback") | ||
} | ||
|
||
func cgoCall() { | ||
C.cgoCallback() | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
// Unless explicitly stated otherwise all files in this repository are licensed | ||
// under the Apache License Version 2.0. | ||
// This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
// Copyright 2023-present Datadog, Inc. | ||
|
||
package gls | ||
|
||
import ( | ||
"runtime" | ||
"sync" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
//dd:orchestrion-enabled | ||
const orchestrionEnabled = false | ||
|
||
func TestSimple(t *testing.T) { | ||
expected := "Hello, World!" | ||
|
||
set(expected) | ||
actual := get() | ||
|
||
if orchestrionEnabled { | ||
t.Log("Orchestrion IS enabled") | ||
require.Equal(t, expected, actual) | ||
} else { | ||
t.Log("Orchestrion IS NOT enabled") | ||
require.Nil(t, actual) | ||
} | ||
} | ||
|
||
// TestCGO tests that the GLS is correctly set even when the code comes from a cgo callback. | ||
func TestCGO(t *testing.T) { | ||
if !orchestrionEnabled { | ||
t.Skip("Orchestrion is not enabled") | ||
} | ||
|
||
expected := "I am inside a cgo callback" | ||
set(nil) | ||
cgoCall() | ||
require.Equal(t, expected, get()) | ||
} | ||
|
||
func TestConcurrency(t *testing.T) { | ||
if !orchestrionEnabled { | ||
t.Skip("Orchestrion is not enabled") | ||
} | ||
|
||
nbSets := 5000 | ||
nbGoRoutines := 300 | ||
|
||
var wg sync.WaitGroup | ||
|
||
wg.Add(nbGoRoutines) | ||
for i := 0; i < nbGoRoutines; i++ { | ||
go func() { | ||
defer wg.Done() | ||
for j := 0; j < nbSets; j++ { | ||
set(j) | ||
require.Equal(t, j, get()) | ||
} | ||
}() | ||
} | ||
|
||
wg.Wait() | ||
} | ||
|
||
func BenchmarkGLS(b *testing.B) { | ||
if !orchestrionEnabled { | ||
b.Skip("Orchestrion is not enabled") | ||
} | ||
|
||
b.Run("Set", func(b *testing.B) { | ||
for i := 0; i < b.N; i++ { | ||
set(i) | ||
} | ||
}) | ||
|
||
b.Run("Get", func(b *testing.B) { | ||
for i := 0; i < b.N; i++ { | ||
runtime.KeepAlive(get()) | ||
} | ||
}) | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// Unless explicitly stated otherwise all files in this repository are licensed | ||
// under the Apache License Version 2.0. | ||
// This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
// Copyright 2023-present Datadog, Inc. | ||
|
||
//go:build unix | ||
|
||
package gls | ||
|
||
import ( | ||
"os" | ||
"os/signal" | ||
"syscall" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
// TestSignal tests that the GLS is correctly set even when the code comes from a signal handler. | ||
func TestSignal(t *testing.T) { | ||
if !orchestrionEnabled { | ||
t.Skip("Orchestrion is not enabled") | ||
} | ||
|
||
expected := "I am inside a signal handler" | ||
|
||
set(nil) | ||
|
||
doneSigChan := make(chan struct{}, 1) | ||
checkChan := make(chan struct{}, 1) | ||
doneCheckChan := make(chan struct{}, 1) | ||
sigChan := make(chan os.Signal, 1) | ||
signal.Notify(sigChan, syscall.SIGUSR1) | ||
|
||
go func() { | ||
<-sigChan | ||
set(expected) | ||
doneSigChan <- struct{}{} | ||
|
||
<-checkChan | ||
require.Equal(t, expected, get()) | ||
doneCheckChan <- struct{}{} | ||
}() | ||
|
||
syscall.Kill(syscall.Getpid(), syscall.SIGUSR1) | ||
<-doneSigChan | ||
checkChan <- struct{}{} | ||
<-doneCheckChan | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains 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