Skip to main content
Tweeted twitter.com/StackCodeReview/status/1585013328715419649
Became Hot Network Question
added 57 characters in body
Source Link
#include <cstddef>
#include <array>
#include <atomic>
#include <utility>
using Handle = uint16_t;
struct HandleTable {
   HandleTable() { clear(); }
   ~HandleTable() { clear(); }

    // Tries to bind a pointer to a handle
    Handle try_bind(void* ptr) {
        // The virtual address is unique for a request in flight
        std::hash<uintptr_t> hash;
        uintptr_t offset = hash(reinterpret_cast<uintptr_t>(ptr)); 
        for(size_t i = 0; i < slots.size(); ++i) {
            size_t idx = (offset + i) % slots.size();

            // Is the current slot occupied (should this be acquire?)
            // Producers don't touch this object after submitting
            void* current = slots[idx].load(std::memory_order_relaxed);
            if(current) { continue; }

            // Try to claim the slot
            // Synchronize with "acquire" in getter functions
            if(slots[idx].compare_exchange_strong(current, ptr, 
                std::memory_order_release, std::memory_order_relaxed)) {
                return Handle(idx + 1);
            }
        }

        return Handle(0);
    }

    // Destroys a handle (called from IRQ)
    void release(Handle val) {
        // Does this need to be memory_order_release?
        // We don't need to synchronize non-atomic data here
        slots[val + 1].store(nullptr, std::memory_order_relaxed);
    }

    // Gets the pointer referred to by handle
    void* get(Handle val) {
       // Synchronize with "release" in setter
       return slots[val + 1].load(std::memory_order_acquire);
    }

   // Clears the handle table (not thread safe)
   void clear() {
       for(size_t i = 0; i < slots.size(); ++i) {
           if(void* res = slots[i].load(std::memory_order_relaxed)) {
               // Synchronise "release" from setter
               std::atomic_thread_fence(std::memory_order_acquire);
               // Do something with the dangling handle
           }
       }

       // Syncrhonise any changes before clearing the table
       std::atomic_thread_fence(std::memory_order_release);
       for(auto& val : slots) { 
           val.store(nullptr, std::memory_order_relaxed); 
      }
   }

   std::array<std::atomic<void*>, 1024> slots;
};
#include <cstddef>
#include <array>
#include <atomic>
using Handle = uint16_t;
struct HandleTable {
   HandleTable() { clear(); }
   ~HandleTable() { clear(); }

    // Tries to bind a pointer to a handle
    Handle try_bind(void* ptr) {
        // The virtual address is unique for a request in flight
        uintptr_t offset = hash(reinterpret_cast<uintptr_t>(ptr)); 
        for(size_t i = 0; i < slots.size(); ++i) {
            size_t idx = (offset + i) % slots.size();

            // Is the current slot occupied (should this be acquire?)
            // Producers don't touch this object after submitting
            void* current = slots[idx].load(std::memory_order_relaxed);
            if(current) { continue; }

            // Try to claim the slot
            // Synchronize with "acquire" in getter functions
            if(slots[idx].compare_exchange_strong(current, ptr, 
                std::memory_order_release, std::memory_order_relaxed)) {
                return Handle(idx + 1);
            }
        }

        return Handle(0);
    }

    // Destroys a handle (called from IRQ)
    void release(Handle val) {
        // Does this need to be memory_order_release?
        // We don't need to synchronize non-atomic data here
        slots[val + 1].store(nullptr, std::memory_order_relaxed);
    }

    // Gets the pointer referred to by handle
    void* get(Handle val) {
       // Synchronize with "release" in setter
       return slots[val + 1].load(std::memory_order_acquire);
    }

   // Clears the handle table (not thread safe)
   void clear() {
       for(size_t i = 0; i < slots.size(); ++i) {
           if(void* res = slots[i].load(std::memory_order_relaxed) {
               // Synchronise "release" from setter
               std::atomic_thread_fence(std::memory_order_acquire);
               // Do something with the dangling handle
           }
       }

       // Syncrhonise any changes before clearing the table
       std::atomic_thread_fence(std::memory_order_release);
       for(auto& val : slots) { 
           val.store(nullptr, std::memory_order_relaxed); 
      }
   }

   std::array<std::atomic<void*>, 1024> slots;
};
#include <cstddef>
#include <array>
#include <atomic>
#include <utility>
using Handle = uint16_t;
struct HandleTable {
   HandleTable() { clear(); }
   ~HandleTable() { clear(); }

    // Tries to bind a pointer to a handle
    Handle try_bind(void* ptr) {
        // The virtual address is unique for a request in flight
        std::hash<uintptr_t> hash;
        uintptr_t offset = hash(reinterpret_cast<uintptr_t>(ptr));
        for(size_t i = 0; i < slots.size(); ++i) {
            size_t idx = (offset + i) % slots.size();

            // Is the current slot occupied (should this be acquire?)
            // Producers don't touch this object after submitting
            void* current = slots[idx].load(std::memory_order_relaxed);
            if(current) { continue; }

            // Try to claim the slot
            // Synchronize with "acquire" in getter functions
            if(slots[idx].compare_exchange_strong(current, ptr,
                std::memory_order_release, std::memory_order_relaxed)) {
                return Handle(idx + 1);
            }
        }

        return Handle(0);
    }

    // Destroys a handle (called from IRQ)
    void release(Handle val) {
        // Does this need to be memory_order_release?
        // We don't need to synchronize non-atomic data here
        slots[val + 1].store(nullptr, std::memory_order_relaxed);
    }

    // Gets the pointer referred to by handle
    void* get(Handle val) {
       // Synchronize with "release" in setter
       return slots[val + 1].load(std::memory_order_acquire);
    }

   // Clears the handle table (not thread safe)
   void clear() {
       for(size_t i = 0; i < slots.size(); ++i) {
           if(void* res = slots[i].load(std::memory_order_relaxed)) {
               // Synchronise "release" from setter
               std::atomic_thread_fence(std::memory_order_acquire);
               // Do something with the dangling handle
           }
       }

       // Syncrhonise any changes before clearing the table
       std::atomic_thread_fence(std::memory_order_release);
       for(auto& val : slots) {
           val.store(nullptr, std::memory_order_relaxed);
      }
   }

 std::array<std::atomic<void*>, 1024> slots;
};
added 57 characters in body
Source Link
#include <cstddef>
#include <array>
#include <atomic>
using Handle = uint16_t;
struct HandleTable {
   HandleTable() { clear(); }
   ~HandleTable() { clear(); }

    // Tries to bind a pointer to a handle
    Handle try_bind(void* ptr) {
        // The virtual address is unique for a request in flight
        uintptr_t offset = hash(reinterpret_cast<uintptr_t>(ptr)); 
        for(size_t i = 0; i < slots.size(); ++i) {
            size_t idx = (offset + i) % slots.size();

            // Is the current slot occupied (should this be acquire?)
            // Producers don't touch this object after submitting
            void* current = slots[idx].load(std::memory_order_relaxed);
            if(current) { continue; }

            // Try to claim the slot
            // Synchronize with "acquire" in getter functions
            if(slots[idx].compare_exchange_strong(current, ptr, 
                std::memory_order_release, std::memory_order_relaxed)) {
                return Handle(idx + 1);
            }
        }

        return Handle(0);
    }

    // Destroys a handle (called from IRQ)
    void release(Handle val) {
        // Does this need to be memory_order_release?
        // We don't need to synchronize non-atomic data here
        slots[val + 1].store(nullptr, std::memory_order_relaxed);
    }

    // Gets the pointer referred to by handle
    void* get(Handle val) {
       // Synchronize with "release" in setter
       return slots[val + 1].load(std::memory_order_acquire);
    }

   // Clears the handle table (not thread safe)
   void clear() {
       for(size_t i = 0; i < slots.size(); ++i) {
           if(void* res = slotsslots[i].load(std::memory_order_relaxed) {
               // Synchronise "release" from setter
               std::atomic_thread_fence(std::memory_order_acquire);
               // Do something with the dangling handle
           }
       }

       // Syncrhonise any changes before clearing the table
       std::atomic_thread_fence(std::memory_order_release);
       for(auto& val : slots) { 
           val.store(nullptr, std::memory_order_relaxed); 
      }
   }

   std::array<std::atomic<void*>, 1024> slots;
};
using Handle = uint16_t;
struct HandleTable {
   HandleTable() { clear(); }
   ~HandleTable() { clear(); }

    // Tries to bind a pointer to a handle
    Handle try_bind(void* ptr) {
        // The virtual address is unique for a request in flight
        uintptr_t offset = hash(reinterpret_cast<uintptr_t>(ptr)); 
        for(size_t i = 0; i < slots.size(); ++i) {
            size_t idx = (offset + i) % slots.size();

            // Is the current slot occupied (should this be acquire?)
            // Producers don't touch this object after submitting
            void* current = slots[idx].load(std::memory_order_relaxed);
            if(current) { continue; }

            // Try to claim the slot
            // Synchronize with "acquire" in getter functions
            if(slots[idx].compare_exchange_strong(current, ptr, 
                std::memory_order_release, std::memory_order_relaxed)) {
                return Handle(idx + 1);
            }
        }

        return Handle(0);
    }

    // Destroys a handle (called from IRQ)
    void release(Handle val) {
        // Does this need to be memory_order_release?
        // We don't need to synchronize non-atomic data here
        slots[val + 1].store(nullptr, std::memory_order_relaxed);
    }

    // Gets the pointer referred to by handle
    void* get(Handle val) {
       // Synchronize with "release" in setter
       return slots[val + 1].load(std::memory_order_acquire);
    }

   // Clears the handle table (not thread safe)
   void clear() {
       for(size_t i = 0; i < slots.size() ++i) {
           if(void* res = slots.load(std::memory_order_relaxed) {
               // Synchronise "release" from setter
               std::atomic_thread_fence(std::memory_order_acquire);
               // Do something with the dangling handle
           }
       }

       // Syncrhonise any changes before clearing the table
       std::atomic_thread_fence(std::memory_order_release);
       for(auto& val : slots) { 
           val.store(nullptr, std::memory_order_relaxed); 
      }
   }

   std::array<std::atomic<void*>, 1024> slots;
};
#include <cstddef>
#include <array>
#include <atomic>
using Handle = uint16_t;
struct HandleTable {
   HandleTable() { clear(); }
   ~HandleTable() { clear(); }

    // Tries to bind a pointer to a handle
    Handle try_bind(void* ptr) {
        // The virtual address is unique for a request in flight
        uintptr_t offset = hash(reinterpret_cast<uintptr_t>(ptr)); 
        for(size_t i = 0; i < slots.size(); ++i) {
            size_t idx = (offset + i) % slots.size();

            // Is the current slot occupied (should this be acquire?)
            // Producers don't touch this object after submitting
            void* current = slots[idx].load(std::memory_order_relaxed);
            if(current) { continue; }

            // Try to claim the slot
            // Synchronize with "acquire" in getter functions
            if(slots[idx].compare_exchange_strong(current, ptr, 
                std::memory_order_release, std::memory_order_relaxed)) {
                return Handle(idx + 1);
            }
        }

        return Handle(0);
    }

    // Destroys a handle (called from IRQ)
    void release(Handle val) {
        // Does this need to be memory_order_release?
        // We don't need to synchronize non-atomic data here
        slots[val + 1].store(nullptr, std::memory_order_relaxed);
    }

    // Gets the pointer referred to by handle
    void* get(Handle val) {
       // Synchronize with "release" in setter
       return slots[val + 1].load(std::memory_order_acquire);
    }

   // Clears the handle table (not thread safe)
   void clear() {
       for(size_t i = 0; i < slots.size(); ++i) {
           if(void* res = slots[i].load(std::memory_order_relaxed) {
               // Synchronise "release" from setter
               std::atomic_thread_fence(std::memory_order_acquire);
               // Do something with the dangling handle
           }
       }

       // Syncrhonise any changes before clearing the table
       std::atomic_thread_fence(std::memory_order_release);
       for(auto& val : slots) { 
           val.store(nullptr, std::memory_order_relaxed); 
      }
   }

   std::array<std::atomic<void*>, 1024> slots;
};
deleted 15 characters in body
Source Link
using Handle = uint16_t;
struct HandleTable {
   HandleTable() { clear(); }
   ~HandleTable() { clear(); }

    // Tries to bind a pointer to a handle
    std::optional<Handle>Handle try_bind(void* ptr) {
        // The virtual address is unique for a request in flight
        uintptr_t offset = hash(reinterpret_cast<uintptr_t>(ptr)); 
        for(size_t i = 0; i < slots.size(); ++i) {
            size_t idx = (offset + i) % slots.size();

            // Is the current slot occupied (should this be acquire?)
            // Producers don't touch this object after submitting
            void* current = slots[idx].load(std::memory_order_relaxed);
            if(current) { continue; }

            // Try to claim the slot
            // Synchronize with "acquire" in getter functions
            if(slots[idx].compare_exchange_strong(current, ptr, 
                std::memory_order_release, std::memory_order_relaxed)) {
                return Handle(idx + 1);
            }
        }

        return Handle(0);
    }

    // Destroys a handle (called from IRQ)
    void release(Handle val) {
        // Does this need to be memory_order_release?
        // We don't need to synchronize non-atomic data here
        slots[val + 1].store(nullptr, std::memory_order_relaxed);
    }

    // Gets the pointer referred to by handle
    void* get(Handle val) {
       // Synchronize with "release" in setter
       return slots[val + 1].load(std::memory_order_acquire);
    }

   // Clears the handle table (not thread safe)
   void clear() {
       for(size_t i = 0; i < slots.size() ++i) {
           if(void* res = slots.load(std::memory_order_relaxed) {
               // Synchronise "release" from setter
               std::atomic_thread_fence(std::memory_order_acquire);
               // Do something with the dangling handle
           }
       }

       // Syncrhonise any changes before clearing the table
       std::atomic_thread_fence(std::memory_order_release);
       for(auto& val : slots) { 
           val.store(nullptr, std::memory_order_relaxed); 
      }
   }

   std::array<std::atomic<void*>, 1024> slots;
};
using Handle = uint16_t;
struct HandleTable {
   HandleTable() { clear(); }
   ~HandleTable() { clear(); }

    // Tries to bind a pointer to a handle
    std::optional<Handle> try_bind(void* ptr) {
        // The virtual address is unique for a request in flight
        uintptr_t offset = hash(reinterpret_cast<uintptr_t>(ptr)); 
        for(size_t i = 0; i < slots.size(); ++i) {
            size_t idx = (offset + i) % slots.size();

            // Is the current slot occupied (should this be acquire?)
            // Producers don't touch this object after submitting
            void* current = slots[idx].load(std::memory_order_relaxed);
            if(current) { continue; }

            // Try to claim the slot
            // Synchronize with "acquire" in getter functions
            if(slots[idx].compare_exchange_strong(current, ptr, 
                std::memory_order_release, std::memory_order_relaxed)) {
                return Handle(idx + 1);
            }
        }

        return Handle(0);
    }

    // Destroys a handle (called from IRQ)
    void release(Handle val) {
        // Does this need to be memory_order_release?
        // We don't need to synchronize non-atomic data here
        slots[val + 1].store(nullptr, std::memory_order_relaxed);
    }

    // Gets the pointer referred to by handle
    void* get(Handle val) {
       // Synchronize with "release" in setter
       return slots[val + 1].load(std::memory_order_acquire);
    }

   // Clears the handle table (not thread safe)
   void clear() {
       for(size_t i = 0; i < slots.size() ++i) {
           if(void* res = slots.load(std::memory_order_relaxed) {
               // Synchronise "release" from setter
               std::atomic_thread_fence(std::memory_order_acquire);
               // Do something with the dangling handle
           }
       }

       // Syncrhonise any changes before clearing the table
       std::atomic_thread_fence(std::memory_order_release);
       for(auto& val : slots) { 
           val.store(nullptr, std::memory_order_relaxed); 
      }
   }

   std::array<std::atomic<void*>, 1024> slots;
};
using Handle = uint16_t;
struct HandleTable {
   HandleTable() { clear(); }
   ~HandleTable() { clear(); }

    // Tries to bind a pointer to a handle
    Handle try_bind(void* ptr) {
        // The virtual address is unique for a request in flight
        uintptr_t offset = hash(reinterpret_cast<uintptr_t>(ptr)); 
        for(size_t i = 0; i < slots.size(); ++i) {
            size_t idx = (offset + i) % slots.size();

            // Is the current slot occupied (should this be acquire?)
            // Producers don't touch this object after submitting
            void* current = slots[idx].load(std::memory_order_relaxed);
            if(current) { continue; }

            // Try to claim the slot
            // Synchronize with "acquire" in getter functions
            if(slots[idx].compare_exchange_strong(current, ptr, 
                std::memory_order_release, std::memory_order_relaxed)) {
                return Handle(idx + 1);
            }
        }

        return Handle(0);
    }

    // Destroys a handle (called from IRQ)
    void release(Handle val) {
        // Does this need to be memory_order_release?
        // We don't need to synchronize non-atomic data here
        slots[val + 1].store(nullptr, std::memory_order_relaxed);
    }

    // Gets the pointer referred to by handle
    void* get(Handle val) {
       // Synchronize with "release" in setter
       return slots[val + 1].load(std::memory_order_acquire);
    }

   // Clears the handle table (not thread safe)
   void clear() {
       for(size_t i = 0; i < slots.size() ++i) {
           if(void* res = slots.load(std::memory_order_relaxed) {
               // Synchronise "release" from setter
               std::atomic_thread_fence(std::memory_order_acquire);
               // Do something with the dangling handle
           }
       }

       // Syncrhonise any changes before clearing the table
       std::atomic_thread_fence(std::memory_order_release);
       for(auto& val : slots) { 
           val.store(nullptr, std::memory_order_relaxed); 
      }
   }

   std::array<std::atomic<void*>, 1024> slots;
};
Source Link
Loading