LCOV - code coverage report
Current view: top level - gi - toggle.cpp (source / functions) Coverage Total Hit
Test: gjs- Code Coverage Lines: 98.9 % 93 92
Test Date: 2024-04-20 17:42:51 Functions: 100.0 % 15 15
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 94.4 % 36 34

             Branch data     Line data    Source code
       1                 :             : /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
       2                 :             : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
       3                 :             : // SPDX-FileCopyrightText: 2017 Endless Mobile, Inc.
       4                 :             : // SPDX-FileCopyrightText: 2021 Canonical Ltd.
       5                 :             : // SPDX-FileContributor: Authored by: Philip Chimento <philip@endlessm.com>
       6                 :             : // SPDX-FileContributor: Philip Chimento <philip.chimento@gmail.com>
       7                 :             : // SPDX-FileContributor: Marco Trevisan <marco.trevisan@canonical.com>
       8                 :             : 
       9                 :             : #include <config.h>
      10                 :             : 
      11                 :             : #include <algorithm>  // for find_if
      12                 :             : #include <atomic>
      13                 :             : #include <deque>
      14                 :             : #include <utility>  // for pair
      15                 :             : 
      16                 :             : #include "gi/object.h"
      17                 :             : #include "gi/toggle.h"
      18                 :             : #include "util/log.h"
      19                 :             : 
      20                 :             : /* No-op unless GJS_VERBOSE_ENABLE_LIFECYCLE is defined to 1. */
      21                 :        3408 : inline void debug(const char* did GJS_USED_VERBOSE_LIFECYCLE,
      22                 :             :                   const ObjectInstance* object GJS_USED_VERBOSE_LIFECYCLE) {
      23                 :             :     gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "ToggleQueue %s %p (%s @ %p)", did,
      24                 :             :                         object, object ? g_type_name(object->gtype()) : "",
      25                 :             :                         object ? object->ptr() : nullptr);
      26                 :        3408 : }
      27                 :             : 
      28                 :        8462 : void ToggleQueue::lock() {
      29                 :        8462 :     auto holding_thread = std::thread::id();
      30                 :        8462 :     auto current_thread = std::this_thread::get_id();
      31                 :             : 
      32         [ +  + ]:     1397232 :     while (!m_holder.compare_exchange_weak(holding_thread, current_thread,
      33                 :             :                                            std::memory_order_acquire)) {
      34                 :             :         // In case the current thread is holding the lock, we can just try
      35                 :             :         // again, checking if this is still true and in case continue
      36         [ +  + ]:     1380308 :         if (holding_thread != current_thread)
      37                 :     1377300 :             holding_thread = std::thread::id();
      38                 :             :     }
      39                 :             : 
      40                 :        8462 :     m_holder_ref_count++;
      41                 :        8462 : }
      42                 :             : 
      43                 :        8462 : void ToggleQueue::maybe_unlock() {
      44                 :        8462 :     g_assert(owns_lock() && "Nothing to unlock here");
      45                 :             : 
      46         [ +  + ]:        8462 :     if (!(--m_holder_ref_count))
      47                 :        5454 :         m_holder.store(std::thread::id(), std::memory_order_release);
      48                 :        8462 : }
      49                 :             : 
      50                 :          92 : std::deque<ToggleQueue::Item>::iterator ToggleQueue::find_operation_locked(
      51                 :             :     const ObjectInstance* obj, ToggleQueue::Direction direction) {
      52                 :             :     return std::find_if(
      53                 :         184 :         q.begin(), q.end(), [obj, direction](const Item& item) -> bool {
      54   [ +  +  +  + ]:          57 :             return item.object == obj && item.direction == direction;
      55                 :         184 :         });
      56                 :             : }
      57                 :             : 
      58                 :             : std::deque<ToggleQueue::Item>::const_iterator
      59                 :        8138 : ToggleQueue::find_operation_locked(const ObjectInstance* obj,
      60                 :             :                                    ToggleQueue::Direction direction) const {
      61                 :             :     return std::find_if(
      62                 :       16276 :         q.begin(), q.end(), [obj, direction](const Item& item) -> bool {
      63   [ +  +  +  + ]:          53 :             return item.object == obj && item.direction == direction;
      64                 :       16276 :         });
      65                 :             : }
      66                 :             : 
      67                 :         758 : void ToggleQueue::handle_all_toggles(Handler handler) {
      68                 :         758 :     g_assert(owns_lock() && "Unsafe access to queue");
      69         [ +  + ]:         780 :     while (handle_toggle(handler))
      70                 :             :         ;
      71                 :         758 : }
      72                 :             : 
      73                 :             : gboolean
      74                 :          21 : ToggleQueue::idle_handle_toggle(void *data)
      75                 :             : {
      76                 :          21 :     auto self = Locked(static_cast<ToggleQueue*>(data));
      77                 :          21 :     self->handle_all_toggles(self->m_toggle_handler);
      78                 :             : 
      79                 :          42 :     return G_SOURCE_REMOVE;
      80                 :          21 : }
      81                 :             : 
      82                 :             : void
      83                 :          32 : ToggleQueue::idle_destroy_notify(void *data)
      84                 :             : {
      85                 :          32 :     auto self = Locked(static_cast<ToggleQueue*>(data));
      86                 :          32 :     self->m_idle_id = 0;
      87                 :          32 :     self->m_toggle_handler = nullptr;
      88                 :          32 : }
      89                 :             : 
      90                 :        4069 : std::pair<bool, bool> ToggleQueue::is_queued(ObjectInstance* obj) const {
      91                 :        4069 :     g_assert(owns_lock() && "Unsafe access to queue");
      92                 :        4069 :     bool has_toggle_down = find_operation_locked(obj, DOWN) != q.end();
      93                 :        4069 :     bool has_toggle_up = find_operation_locked(obj, UP) != q.end();
      94                 :        4069 :     return {has_toggle_down, has_toggle_up};
      95                 :             : }
      96                 :             : 
      97                 :        3055 : std::pair<bool, bool> ToggleQueue::cancel(ObjectInstance* obj) {
      98                 :        3055 :     debug("cancel", obj);
      99                 :        3055 :     g_assert(owns_lock() && "Unsafe access to queue");
     100                 :        3055 :     bool had_toggle_down = false;
     101                 :        3055 :     bool had_toggle_up = false;
     102                 :             : 
     103         [ +  + ]:        3063 :     for (auto it = q.begin(); it != q.end();) {
     104         [ +  - ]:           8 :         if (it->object == obj) {
     105                 :           8 :             had_toggle_down |= (it->direction == Direction::DOWN);
     106                 :           8 :             had_toggle_up |= (it->direction == Direction::UP);
     107                 :           8 :             it = q.erase(it);
     108                 :           8 :             continue;
     109                 :             :         }
     110                 :           0 :         it++;
     111                 :             :     }
     112                 :             : 
     113                 :             :     gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "ToggleQueue: %p (%p) was %s", obj,
     114                 :             :                         obj ? obj->ptr() : nullptr,
     115                 :             :                         had_toggle_down && had_toggle_up
     116                 :             :                             ? "queued to toggle BOTH"
     117                 :             :                         : had_toggle_down ? "queued to toggle DOWN"
     118                 :             :                         : had_toggle_up   ? "queued to toggle UP"
     119                 :             :                                           : "not queued");
     120                 :        3055 :     return {had_toggle_down, had_toggle_up};
     121                 :             : }
     122                 :             : 
     123                 :         780 : bool ToggleQueue::handle_toggle(Handler handler) {
     124                 :         780 :     g_assert(owns_lock() && "Unsafe access to queue");
     125                 :             : 
     126         [ +  + ]:         780 :     if (q.empty())
     127                 :         758 :         return false;
     128                 :             : 
     129                 :          22 :     auto const& item = q.front();
     130         [ +  + ]:          22 :     if (item.direction == UP)
     131                 :          17 :         debug("handle UP", item.object);
     132                 :             :     else
     133                 :           5 :         debug("handle DOWN", item.object);
     134                 :             : 
     135                 :          22 :     handler(item.object, item.direction);
     136                 :          22 :     q.pop_front();
     137                 :             : 
     138                 :          22 :     return true;
     139                 :             : }
     140                 :             : 
     141                 :             : void
     142                 :         239 : ToggleQueue::shutdown(void)
     143                 :             : {
     144                 :         239 :     debug("shutdown", nullptr);
     145                 :         239 :     g_assert(((void)"Queue should have been emptied before shutting down",
     146                 :             :               q.empty()));
     147                 :         239 :     m_shutdown = true;
     148                 :         239 : }
     149                 :             : 
     150                 :          94 : void ToggleQueue::enqueue(ObjectInstance* obj, ToggleQueue::Direction direction,
     151                 :             :                           // https://trac.cppcheck.net/ticket/10733
     152                 :             :                           // cppcheck-suppress passedByValue
     153                 :             :                           ToggleQueue::Handler handler) {
     154                 :          94 :     g_assert(owns_lock() && "Unsafe access to queue");
     155                 :             : 
     156         [ +  + ]:          94 :     if (G_UNLIKELY (m_shutdown)) {
     157                 :           4 :         gjs_debug(GJS_DEBUG_GOBJECT,
     158                 :             :                   "Enqueuing GObject %p to toggle %s after "
     159                 :             :                   "shutdown, probably from another thread (%p).",
     160         [ +  - ]:           2 :                   obj->ptr(), direction == UP ? "UP" : "DOWN", g_thread_self());
     161                 :          62 :         return;
     162                 :             :     }
     163                 :             : 
     164                 :          92 :     auto other_item = find_operation_locked(obj, direction == UP ? DOWN : UP);
     165         [ +  + ]:          92 :     if (other_item != q.end()) {
     166         [ +  + ]:          31 :         if (direction == UP) {
     167                 :           5 :             debug("enqueue UP, dequeuing already DOWN object", obj);
     168                 :             :         } else {
     169                 :          26 :             debug("enqueue DOWN, dequeuing already UP object", obj);
     170                 :             :         }
     171                 :          31 :         q.erase(other_item);
     172                 :          31 :         return;
     173                 :             :     }
     174                 :             : 
     175                 :             :     /* Only keep an unowned reference on the object here, as if we're here, the
     176                 :             :      * JSObject wrapper has already a reference and we don't want to cause
     177                 :             :      * any weak notify in case it has lost one already in the main thread.
     178                 :             :      * So let's just save the pointer to keep track of the object till we
     179                 :             :      * don't handle this toggle.
     180                 :             :      * We rely on object's cancelling the queue in case an object gets
     181                 :             :      * finalized earlier than we've processed it.
     182                 :             :      */
     183                 :          61 :     q.emplace_back(obj, direction);
     184                 :             : 
     185         [ +  + ]:          61 :     if (direction == UP) {
     186                 :          51 :         debug("enqueue UP", obj);
     187                 :             :     } else {
     188                 :          10 :         debug("enqueue DOWN", obj);
     189                 :             :     }
     190                 :             : 
     191         [ +  + ]:          61 :     if (m_idle_id) {
     192                 :          29 :         g_assert(((void) "Should always enqueue with the same handler",
     193                 :             :                   m_toggle_handler == handler));
     194                 :          29 :         return;
     195                 :             :     }
     196                 :             : 
     197                 :          32 :     m_toggle_handler = handler;
     198                 :          32 :     m_idle_id = g_idle_add_full(G_PRIORITY_HIGH, idle_handle_toggle, this,
     199                 :             :                                 idle_destroy_notify);
     200                 :             : }
        

Generated by: LCOV version 2.0-1