Code Monkey home page Code Monkey logo

lock_free's People

Contributors

lingxi-li avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

lock_free's Issues

DefaultConstructible and resettable allocator

It may not always be convenient to supply capacity at construction time. For example, when the allocator is a static data member. Allocator should be DefaultConstructible and be able to be reset with a sensible capacity at a later post-construction time.

Flawed memory order specification in queue

With current memory order specification, no happens-before requirement is imposed between head and tail update. Consider try_pop(). hold_ptr() ensures that the latest value of head is read, yet any value in the modification order of tail can be read. Therefore, head != tail doesn't necessarily mean a non-empty queue.

Let's try to establish order between head and tail.

  1. Initially, head == tail.
  2. The first thread that successfully pops is OK. It sees a tail which is not so outdated that breaks any invariant. The pop should issue a release on head.
  3. Any later popping thread needs to hold head before doing anything else. Let hold_ptr() issue an acquire on head. Since it reads the latest value of head, it's therefore guaranteed to acquire the release issued in 2, and sees a tail which is not so outdated that breaks any invariant.

Move allocator to sub-namespace cap

allocator is used to support efficient implementation of fixed-capacity/capacitated data structures. Create a sub-namespace cap (capacitated) for these data structures and move allocator to it.

Adopt C++17 and leverage std::is_aggregate

C++17 has some killing features that I cannot simply ignore. std::is_aggregate is one of them.

This concerns uniform initialization. By that, you may think I'm talking about the {} syntax. But I don't. What do you expect std::make_unique<std::vector<int>>(2, 1) to make, [1, 1] or [2, 1]? Think about it and you see the issue. It may be problematic if we simply stick to {} as a kind of uniform initialization. We cannot stick to () either, for it doesn't work for aggregate types (unless it's empty). In my humble opinion, genuine uniform initialization should do the following:

  1. For non-aggregate type, use () syntax.
  2. For aggregate type, use {} syntax.

With std::is_aggregate introduced in C++17, implementing this genuine uniform initialization becomes straightforward.

Now, you may wonder why use aggregate types in the first place? Well, I don't bother writing boilerplate member-wise constructors, and want to leverage aggregate initialization. For example, instead of

struct stru {
  stru(int a = 0, int b = 0, int c = 0) noexcept:
    a(a), b(b), c(c) {
  }

  int a, b, c;
};
stru s0, s1(1), s2(1, 2);

why not simply

struct stru {
  int a, b, c;
};
stru s0{}, s1{1}, s2{1, 2};

allocator is subject to ABA problem

For example, allocator::allocate() is currently implemented as

  T* allocate(std::size_t = 1) {
    auto oldhead = head.load(acq);
    do {
      if (!oldhead) throw std::bad_alloc{};
    }
    while (!head.compare_exchange_weak(oldhead, oldhead->next, rlx, acq));
    return std::addressof(oldhead->data);
  }

When head compares equal to oldhead, oldhead->next may already be outdated and invalid.

Notes on allocator

allocate()
head.trefcnt counts the number of successful pops. It follows that when head == oldhead, nothing has happened in the meantime: 1) No pop happened, otherwise head.trefcnt would have changed; 2) no push happened, otherwise head.p would have changed. Since compare_exchange() compares the latest value, if successful, oldhead is the latest value. oldhead is read with acquire, and thus acquires the results of the latest push. oldhead.p->next.load(rlx) is therefore latest. A successful compare_exchange() thus correctly delinks the current head. Successful compare_exchange() can be a relaxed operation, it only delinks head, and doesn't modify anything to the node.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.