| /* | |
| * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| #include "gc/z/zDriverPort.hpp" | |
| #include "gc/z/zFuture.inline.hpp" | |
| #include "gc/z/zList.inline.hpp" | |
| #include "gc/z/zLock.inline.hpp" | |
| #include "utilities/debug.hpp" | |
| ZDriverRequest::ZDriverRequest() | |
| : ZDriverRequest(GCCause::_no_gc, 0, 0) {} | |
| ZDriverRequest::ZDriverRequest(GCCause::Cause cause, uint young_nworkers, uint old_nworkers) | |
| : _cause(cause), | |
| _young_nworkers(young_nworkers), | |
| _old_nworkers(old_nworkers) {} | |
| bool ZDriverRequest::operator==(const ZDriverRequest& other) const { | |
| return _cause == other._cause; | |
| } | |
| GCCause::Cause ZDriverRequest::cause() const { | |
| return _cause; | |
| } | |
| uint ZDriverRequest::young_nworkers() const { | |
| return _young_nworkers; | |
| } | |
| uint ZDriverRequest::old_nworkers() const { | |
| return _old_nworkers; | |
| } | |
| class ZDriverPortEntry { | |
| friend class ZList<ZDriverPortEntry>; | |
| private: | |
| const ZDriverRequest _message; | |
| uint64_t _seqnum; | |
| ZFuture<ZDriverRequest> _result; | |
| ZListNode<ZDriverPortEntry> _node; | |
| public: | |
| ZDriverPortEntry(const ZDriverRequest& message) | |
| : _message(message), | |
| _seqnum(0) {} | |
| void set_seqnum(uint64_t seqnum) { | |
| _seqnum = seqnum; | |
| } | |
| uint64_t seqnum() const { | |
| return _seqnum; | |
| } | |
| ZDriverRequest message() const { | |
| return _message; | |
| } | |
| void wait() { | |
| const ZDriverRequest message = _result.get(); | |
| assert(message == _message, "Message mismatch"); | |
| } | |
| void satisfy(const ZDriverRequest& message) { | |
| _result.set(message); | |
| } | |
| }; | |
| ZDriverPort::ZDriverPort() | |
| : _lock(), | |
| _has_message(false), | |
| _seqnum(0), | |
| _queue() {} | |
| bool ZDriverPort::is_busy() const { | |
| ZLocker<ZConditionLock> locker(&_lock); | |
| return _has_message; | |
| } | |
| void ZDriverPort::send_sync(const ZDriverRequest& message) { | |
| ZDriverPortEntry entry(message); | |
| { | |
| // Enqueue message | |
| ZLocker<ZConditionLock> locker(&_lock); | |
| entry.set_seqnum(_seqnum); | |
| _queue.insert_last(&entry); | |
| _lock.notify(); | |
| } | |
| // Wait for completion | |
| entry.wait(); | |
| { | |
| // Guard deletion of underlying semaphore. This is a workaround for a | |
| // bug in sem_post() in glibc < 2.21, where it's not safe to destroy | |
| // the semaphore immediately after returning from sem_wait(). The | |
| // reason is that sem_post() can touch the semaphore after a waiting | |
| // thread have returned from sem_wait(). To avoid this race we are | |
| // forcing the waiting thread to acquire/release the lock held by the | |
| // posting thread. https://sourceware.org/bugzilla/show_bug.cgi?id=12674 | |
| ZLocker<ZConditionLock> locker(&_lock); | |
| } | |
| } | |
| void ZDriverPort::send_async(const ZDriverRequest& message) { | |
| ZLocker<ZConditionLock> locker(&_lock); | |
| if (!_has_message) { | |
| // Post message | |
| _message = message; | |
| _has_message = true; | |
| _lock.notify(); | |
| } | |
| } | |
| ZDriverRequest ZDriverPort::receive() { | |
| ZLocker<ZConditionLock> locker(&_lock); | |
| // Wait for message | |
| while (!_has_message && _queue.is_empty()) { | |
| _lock.wait(); | |
| } | |
| // Increment request sequence number | |
| _seqnum++; | |
| if (!_has_message) { | |
| // Message available in the queue | |
| _message = _queue.first()->message(); | |
| _has_message = true; | |
| } | |
| return _message; | |
| } | |
| void ZDriverPort::ack() { | |
| ZLocker<ZConditionLock> locker(&_lock); | |
| if (!_has_message) { | |
| // Nothing to ack | |
| return; | |
| } | |
| // Satisfy requests (and duplicates) in queue | |
| ZListIterator<ZDriverPortEntry> iter(&_queue); | |
| for (ZDriverPortEntry* entry; iter.next(&entry);) { | |
| if (entry->message() == _message && entry->seqnum() < _seqnum) { | |
| // Dequeue and satisfy request. Note that the dequeue operation must | |
| // happen first, since the request will immediately be deallocated | |
| // once it has been satisfied. | |
| _queue.remove(entry); | |
| entry->satisfy(_message); | |
| } | |
| } | |
| if (_queue.is_empty()) { | |
| // Queue is empty | |
| _has_message = false; | |
| } else { | |
| // Post first message in queue | |
| _message = _queue.first()->message(); | |
| } | |
| } |