Skip to content

Basic: use std::atomic instead of home-grown synch #3006

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

Merged
merged 1 commit into from
Jun 20, 2016

Conversation

compnerd
Copy link
Member

What's in this pull request?

Resolved bug number: (SR-)


Before merging this pull request to apple/swift repository:

  • Test pull request on Swift continuous integration.

Triggering Swift CI

The swift-ci is triggered by writing a comment on this PR addressed to the GitHub user @swift-ci. Different tests will run depending on the specific comment that you use. The currently available comments are:

Smoke Testing

Platform Comment
All supported platforms @swift-ci Please smoke test
All supported platforms @swift-ci Please smoke test and merge
OS X platform @swift-ci Please smoke test OS X platform
Linux platform @swift-ci Please smoke test Linux platform

Validation Testing

Platform Comment
All supported platforms @swift-ci Please test
All supported platforms @swift-ci Please test and merge
OS X platform @swift-ci Please test OS X platform
OS X platform @swift-ci Please benchmark
Linux platform @swift-ci Please test Linux platform

Lint Testing

Language Comment
Python @swift-ci Please Python lint

Note: Only members of the Apple organization can trigger swift-ci.

SVN r271540 removed the LLVM atomics. Use std::atomic instead. NFC.

@compnerd
Copy link
Member Author

CC @gottesmm

@slavapestov
Copy link
Contributor

@swift-ci Please test

@slavapestov slavapestov self-assigned this Jun 16, 2016
@compnerd
Copy link
Member Author

Ping

}

void Release() const {
int refCount = static_cast<int>(llvm::sys::AtomicDecrement(&ref_cnt));
int refCount =
static_cast<int>(ref_cnt.fetch_sub(1, std::memory_order_relaxed));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this require an acquire so that the destructor is synchronized with the last modification?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that seems safer. However, this is maintaining the status quo (the current behaviour of AtomicDecrement is a memory_order_relaxed). I can do a subsequent change to change it to memory_order_acquire, or we can make it part of this change. I think doing a follow up is better as this change doesnt change semantics that way.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure?.. I'm confused -- the GCC docs say "In most cases, these builtins are considered a full barrier." for __sync_add_and_fetch.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yeah, reading over the docs, it does say that. Ill update the patch since that seems like it should be acquire in light of the docs.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems that all of them need to be acquire as both the __sync_add_and_fetch and __sync_sub_and_fetch which these were implemented with do full barriers.

@atrick
Copy link
Contributor

atrick commented Jun 19, 2016

  • @gparker42
    Why is retain an "acquire"? Is this refcounting supposed to protect the object's lifetime in the presence of races?

Regardless, "retain" should definitely be a "release" and should only be an "acquire" when the count is zero.

@compnerd
Copy link
Member Author

@atrick Im confused like @gribozavr now. Am I misunderstanding something, the docs indicate that both the __sync_add_and_fetch and __sync_sub_and_fetch do a full barrier. Isn't that memory_order_acquire semantics? If the retain should ensure no re-ordering occurs after the store, then it sounds like we want a memory_order_acq_rel so that nothing before nor after is re-ordered?

@atrick
Copy link
Contributor

atrick commented Jun 19, 2016

Yes, it looks like the previous implementation was generating a full barrier.

This change weakens the barrier and is incorrect:

  • ref_cnt.fetch_add(1, std::memory_order_acquire);

...because it is not a release barrier. (you can see on arm64 it's generating stxr, not stlxr). You can conservatively make it acq_rel.

Full barrier on both acquire and release is very innefficient. It's fine for your patch because then it's NFC. Also, I don't know anything about the clients of this class and what additional assumptions they might make.

However, for strong Swift refcounts, we normally want:

  • retain: no barrier
  • release: release barrier on the decrement
  • deinit: acquire barrier before executing the cleanup

It's actually pretty simple. The complexity arises with weak references.

SVN r271540 removed the LLVM atomics.  Use std::atomic instead.  NFC.
@compnerd
Copy link
Member Author

Given what you have stated, yes, this is a bit overkill, but should be the most equivalent change. Updated accordingly.

@gribozavr
Copy link
Contributor

Looks equivalent (to the old code) to me.

@swift-ci Please test and merge

@swift-ci swift-ci merged commit 1af4b02 into swiftlang:master Jun 20, 2016
@compnerd compnerd deleted the atomics branch July 3, 2016 17:08
@benlangmuir
Copy link
Contributor

FYI fetch_sub returns the previous value of ref_cnt, which broke Release(). I fixed it in #3672.

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.

6 participants