1 /* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef SYNC_SEMAPHORE_H 17 #define SYNC_SEMAPHORE_H 18 19 #include <atomic> 20 #include <chrono> 21 #include <thread> 22 #include <mutex> 23 #include <condition_variable> 24 #include <unistd.h> 25 #include <sys/syscall.h> 26 #include <linux/futex.h> 27 #include "sched/execute_ctx.h" 28 #include "delayed_worker.h" 29 30 namespace ffrt { 31 class semaphore { 32 uint64_t data; 33 34 public: semaphore()35 semaphore() : data(0) 36 { 37 } semaphore(uint32_t d)38 semaphore(uint32_t d) : data(d) 39 { 40 } 41 semaphore(semaphore const&) = delete; 42 void operator=(semaphore const&) = delete; 43 acquire()44 void acquire() 45 { 46 const uint64_t w = static_cast<uint64_t>(1) << 32; 47 48 uint64_t d = __atomic_fetch_add(&data, w, __ATOMIC_RELAXED); 49 for (;;) { 50 // RunnableTaskNumber == 0 ? 51 if (static_cast<uint32_t>(d) == 0) { 52 // RQ is empty, then sleep 53 syscall(SYS_futex, &data, FUTEX_WAIT_PRIVATE, 0, nullptr, nullptr, 0); 54 d = __atomic_load_n(&data, __ATOMIC_RELAXED); 55 } else if (__atomic_compare_exchange_n(&data, &d, d - 1 - w, 1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) { 56 break; 57 } 58 } 59 } 60 release()61 void release() 62 { 63 // increase RunnableTaskNumber 64 uint64_t d = __atomic_fetch_add(&data, 1, __ATOMIC_RELEASE); 65 // overflow 66 if (static_cast<uint32_t>(d) == ~0u) { 67 abort(); 68 } 69 // if RunnableTaskNumber == 0 && SleepWorkerNumber > 0, then wake up sleep worker 70 // if RQ is empty before new task is enqueued, RunnableTaskNumber is from 0 to 1 71 if (static_cast<uint32_t>(d) == 0 && (d >> 32) > 0) { 72 syscall(SYS_futex, &data, FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0); 73 } 74 75 // if RunnableTaskNumber >= 4 && SleepWorkerNumber > 0 , then wake up another sleep worker 76 if (static_cast<uint32_t>(d) >= 4 && (d >> 32) > 0) { 77 syscall(SYS_futex, &data, FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0); 78 } 79 } 80 }; 81 } // namespace ffrt 82 #endif 83