Spin-lock implementation
$begingroup$
I'm working on a project where a spinlock is more appropriate than a mutex, and after few tries I came up with:
type SpinLock uint32
func (sl *SpinLock) Lock() {
for !atomic.CompareAndSwapUint32((*uint32)(sl), 0, 1) {
runtime.Gosched() //without this it locks up on GOMAXPROCS > 1
}
}
func (sl *SpinLock) Unlock() {
atomic.StoreUint32((*uint32)(sl), 0)
}
It works fine, and it's even a little bit faster than sync.Mutex
, and 2x the speed of sync.RWMutex
.
➜ go test -bench=. -benchmem -v -cpu 4
BenchmarkSpinL-4 2000 1078798 ns/op 33923 B/op 2006 allocs/op
BenchmarkMutex-4 2000 1195814 ns/op 32781 B/op 2002 allocs/o
BenchmarkRWMutex-4 1000 2352117 ns/op 78253 B/op 2147 allocs/op
The test uses multi readers / writers to a map[int]*struct{int, int}
. Running it with -race
doesn't detect any data races, either.
But I have that nagging feeling that I forgot something, so I'm wondering if my implementation is correct?
locking go
$endgroup$
add a comment |
$begingroup$
I'm working on a project where a spinlock is more appropriate than a mutex, and after few tries I came up with:
type SpinLock uint32
func (sl *SpinLock) Lock() {
for !atomic.CompareAndSwapUint32((*uint32)(sl), 0, 1) {
runtime.Gosched() //without this it locks up on GOMAXPROCS > 1
}
}
func (sl *SpinLock) Unlock() {
atomic.StoreUint32((*uint32)(sl), 0)
}
It works fine, and it's even a little bit faster than sync.Mutex
, and 2x the speed of sync.RWMutex
.
➜ go test -bench=. -benchmem -v -cpu 4
BenchmarkSpinL-4 2000 1078798 ns/op 33923 B/op 2006 allocs/op
BenchmarkMutex-4 2000 1195814 ns/op 32781 B/op 2002 allocs/o
BenchmarkRWMutex-4 1000 2352117 ns/op 78253 B/op 2147 allocs/op
The test uses multi readers / writers to a map[int]*struct{int, int}
. Running it with -race
doesn't detect any data races, either.
But I have that nagging feeling that I forgot something, so I'm wondering if my implementation is correct?
locking go
$endgroup$
add a comment |
$begingroup$
I'm working on a project where a spinlock is more appropriate than a mutex, and after few tries I came up with:
type SpinLock uint32
func (sl *SpinLock) Lock() {
for !atomic.CompareAndSwapUint32((*uint32)(sl), 0, 1) {
runtime.Gosched() //without this it locks up on GOMAXPROCS > 1
}
}
func (sl *SpinLock) Unlock() {
atomic.StoreUint32((*uint32)(sl), 0)
}
It works fine, and it's even a little bit faster than sync.Mutex
, and 2x the speed of sync.RWMutex
.
➜ go test -bench=. -benchmem -v -cpu 4
BenchmarkSpinL-4 2000 1078798 ns/op 33923 B/op 2006 allocs/op
BenchmarkMutex-4 2000 1195814 ns/op 32781 B/op 2002 allocs/o
BenchmarkRWMutex-4 1000 2352117 ns/op 78253 B/op 2147 allocs/op
The test uses multi readers / writers to a map[int]*struct{int, int}
. Running it with -race
doesn't detect any data races, either.
But I have that nagging feeling that I forgot something, so I'm wondering if my implementation is correct?
locking go
$endgroup$
I'm working on a project where a spinlock is more appropriate than a mutex, and after few tries I came up with:
type SpinLock uint32
func (sl *SpinLock) Lock() {
for !atomic.CompareAndSwapUint32((*uint32)(sl), 0, 1) {
runtime.Gosched() //without this it locks up on GOMAXPROCS > 1
}
}
func (sl *SpinLock) Unlock() {
atomic.StoreUint32((*uint32)(sl), 0)
}
It works fine, and it's even a little bit faster than sync.Mutex
, and 2x the speed of sync.RWMutex
.
➜ go test -bench=. -benchmem -v -cpu 4
BenchmarkSpinL-4 2000 1078798 ns/op 33923 B/op 2006 allocs/op
BenchmarkMutex-4 2000 1195814 ns/op 32781 B/op 2002 allocs/o
BenchmarkRWMutex-4 1000 2352117 ns/op 78253 B/op 2147 allocs/op
The test uses multi readers / writers to a map[int]*struct{int, int}
. Running it with -race
doesn't detect any data races, either.
But I have that nagging feeling that I forgot something, so I'm wondering if my implementation is correct?
locking go
locking go
edited 26 mins ago
Jamal♦
30.3k11119227
30.3k11119227
asked Aug 18 '14 at 0:08
OneOfOneOneOfOne
242210
242210
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
$begingroup$
The only weak point is that the implementation is not copy safe nor there exist mechanism for ensuring copy protection. I would hide its underlying type and return as sync.Locker
, so it can't be mis-used:
type spinLock uint32
func (sl *spinLock) Lock() {
for !atomic.CompareAndSwapUint32((*uint32)(sl), 0, 1) {
runtime.Gosched() //without this it locks up on GOMAXPROCS > 1
}
}
func (sl *spinLock) Unlock() {
atomic.StoreUint32((*uint32)(sl), 0)
}
func SpinLock() sync.Locker {
return &spinLock{}
}
An alternative I've seen in sync.Cond
is to embed an auxiliary type for copy-protection, although it would complicate the implementation unnecessary.
$endgroup$
1
$begingroup$
Hi, and welcome to Code Review, and congratulations on passing through the 'Late Answer' review queue. Nice answer to an older question.
$endgroup$
– rolfl♦
Sep 27 '14 at 14:14
$begingroup$
you use theSpinLock
function instantiate and return thespinLock
(async.Locker
) using composite literal notation (return &spinLock{}
), but this inappropriate for the underlying type and so [the compiler throws an error]( play.golang.org/p/qtxa5zYD1b_N). If thespinLock
is instead instantiated via another method, such asreturn new(spinLock)
, then the compiler is happy. Here's a link to an example that compiles.
$endgroup$
– austin_y
12 hours ago
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f60332%2fspin-lock-implementation%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
The only weak point is that the implementation is not copy safe nor there exist mechanism for ensuring copy protection. I would hide its underlying type and return as sync.Locker
, so it can't be mis-used:
type spinLock uint32
func (sl *spinLock) Lock() {
for !atomic.CompareAndSwapUint32((*uint32)(sl), 0, 1) {
runtime.Gosched() //without this it locks up on GOMAXPROCS > 1
}
}
func (sl *spinLock) Unlock() {
atomic.StoreUint32((*uint32)(sl), 0)
}
func SpinLock() sync.Locker {
return &spinLock{}
}
An alternative I've seen in sync.Cond
is to embed an auxiliary type for copy-protection, although it would complicate the implementation unnecessary.
$endgroup$
1
$begingroup$
Hi, and welcome to Code Review, and congratulations on passing through the 'Late Answer' review queue. Nice answer to an older question.
$endgroup$
– rolfl♦
Sep 27 '14 at 14:14
$begingroup$
you use theSpinLock
function instantiate and return thespinLock
(async.Locker
) using composite literal notation (return &spinLock{}
), but this inappropriate for the underlying type and so [the compiler throws an error]( play.golang.org/p/qtxa5zYD1b_N). If thespinLock
is instead instantiated via another method, such asreturn new(spinLock)
, then the compiler is happy. Here's a link to an example that compiles.
$endgroup$
– austin_y
12 hours ago
add a comment |
$begingroup$
The only weak point is that the implementation is not copy safe nor there exist mechanism for ensuring copy protection. I would hide its underlying type and return as sync.Locker
, so it can't be mis-used:
type spinLock uint32
func (sl *spinLock) Lock() {
for !atomic.CompareAndSwapUint32((*uint32)(sl), 0, 1) {
runtime.Gosched() //without this it locks up on GOMAXPROCS > 1
}
}
func (sl *spinLock) Unlock() {
atomic.StoreUint32((*uint32)(sl), 0)
}
func SpinLock() sync.Locker {
return &spinLock{}
}
An alternative I've seen in sync.Cond
is to embed an auxiliary type for copy-protection, although it would complicate the implementation unnecessary.
$endgroup$
1
$begingroup$
Hi, and welcome to Code Review, and congratulations on passing through the 'Late Answer' review queue. Nice answer to an older question.
$endgroup$
– rolfl♦
Sep 27 '14 at 14:14
$begingroup$
you use theSpinLock
function instantiate and return thespinLock
(async.Locker
) using composite literal notation (return &spinLock{}
), but this inappropriate for the underlying type and so [the compiler throws an error]( play.golang.org/p/qtxa5zYD1b_N). If thespinLock
is instead instantiated via another method, such asreturn new(spinLock)
, then the compiler is happy. Here's a link to an example that compiles.
$endgroup$
– austin_y
12 hours ago
add a comment |
$begingroup$
The only weak point is that the implementation is not copy safe nor there exist mechanism for ensuring copy protection. I would hide its underlying type and return as sync.Locker
, so it can't be mis-used:
type spinLock uint32
func (sl *spinLock) Lock() {
for !atomic.CompareAndSwapUint32((*uint32)(sl), 0, 1) {
runtime.Gosched() //without this it locks up on GOMAXPROCS > 1
}
}
func (sl *spinLock) Unlock() {
atomic.StoreUint32((*uint32)(sl), 0)
}
func SpinLock() sync.Locker {
return &spinLock{}
}
An alternative I've seen in sync.Cond
is to embed an auxiliary type for copy-protection, although it would complicate the implementation unnecessary.
$endgroup$
The only weak point is that the implementation is not copy safe nor there exist mechanism for ensuring copy protection. I would hide its underlying type and return as sync.Locker
, so it can't be mis-used:
type spinLock uint32
func (sl *spinLock) Lock() {
for !atomic.CompareAndSwapUint32((*uint32)(sl), 0, 1) {
runtime.Gosched() //without this it locks up on GOMAXPROCS > 1
}
}
func (sl *spinLock) Unlock() {
atomic.StoreUint32((*uint32)(sl), 0)
}
func SpinLock() sync.Locker {
return &spinLock{}
}
An alternative I've seen in sync.Cond
is to embed an auxiliary type for copy-protection, although it would complicate the implementation unnecessary.
answered Sep 27 '14 at 13:44
rjeczalikrjeczalik
762
762
1
$begingroup$
Hi, and welcome to Code Review, and congratulations on passing through the 'Late Answer' review queue. Nice answer to an older question.
$endgroup$
– rolfl♦
Sep 27 '14 at 14:14
$begingroup$
you use theSpinLock
function instantiate and return thespinLock
(async.Locker
) using composite literal notation (return &spinLock{}
), but this inappropriate for the underlying type and so [the compiler throws an error]( play.golang.org/p/qtxa5zYD1b_N). If thespinLock
is instead instantiated via another method, such asreturn new(spinLock)
, then the compiler is happy. Here's a link to an example that compiles.
$endgroup$
– austin_y
12 hours ago
add a comment |
1
$begingroup$
Hi, and welcome to Code Review, and congratulations on passing through the 'Late Answer' review queue. Nice answer to an older question.
$endgroup$
– rolfl♦
Sep 27 '14 at 14:14
$begingroup$
you use theSpinLock
function instantiate and return thespinLock
(async.Locker
) using composite literal notation (return &spinLock{}
), but this inappropriate for the underlying type and so [the compiler throws an error]( play.golang.org/p/qtxa5zYD1b_N). If thespinLock
is instead instantiated via another method, such asreturn new(spinLock)
, then the compiler is happy. Here's a link to an example that compiles.
$endgroup$
– austin_y
12 hours ago
1
1
$begingroup$
Hi, and welcome to Code Review, and congratulations on passing through the 'Late Answer' review queue. Nice answer to an older question.
$endgroup$
– rolfl♦
Sep 27 '14 at 14:14
$begingroup$
Hi, and welcome to Code Review, and congratulations on passing through the 'Late Answer' review queue. Nice answer to an older question.
$endgroup$
– rolfl♦
Sep 27 '14 at 14:14
$begingroup$
you use the
SpinLock
function instantiate and return the spinLock
(a sync.Locker
) using composite literal notation (return &spinLock{}
), but this inappropriate for the underlying type and so [the compiler throws an error]( play.golang.org/p/qtxa5zYD1b_N). If the spinLock
is instead instantiated via another method, such as return new(spinLock)
, then the compiler is happy. Here's a link to an example that compiles.$endgroup$
– austin_y
12 hours ago
$begingroup$
you use the
SpinLock
function instantiate and return the spinLock
(a sync.Locker
) using composite literal notation (return &spinLock{}
), but this inappropriate for the underlying type and so [the compiler throws an error]( play.golang.org/p/qtxa5zYD1b_N). If the spinLock
is instead instantiated via another method, such as return new(spinLock)
, then the compiler is happy. Here's a link to an example that compiles.$endgroup$
– austin_y
12 hours ago
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f60332%2fspin-lock-implementation%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown