Spin-lock implementation












8












$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?










share|improve this question











$endgroup$

















    8












    $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?










    share|improve this question











    $endgroup$















      8












      8








      8


      3



      $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?










      share|improve this question











      $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






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited 26 mins ago









      Jamal

      30.3k11119227




      30.3k11119227










      asked Aug 18 '14 at 0:08









      OneOfOneOneOfOne

      242210




      242210






















          1 Answer
          1






          active

          oldest

          votes


















          6












          $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.






          share|improve this answer









          $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 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













          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
          });


          }
          });














          draft saved

          draft discarded


















          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









          6












          $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.






          share|improve this answer









          $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 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


















          6












          $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.






          share|improve this answer









          $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 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
















          6












          6








          6





          $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.






          share|improve this answer









          $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.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          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 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
















          • 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 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










          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




















          draft saved

          draft discarded




















































          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.




          draft saved


          draft discarded














          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





















































          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







          Popular posts from this blog

          Costa Masnaga

          Fotorealismo

          Sidney Franklin