1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Rosegarden
5     A sequencer and musical notation editor.
6     Copyright 2000-2021 the Rosegarden development team.
7     See the AUTHORS file for more details.
8 
9     This program is free software; you can redistribute it and/or
10     modify it under the terms of the GNU General Public License as
11     published by the Free Software Foundation; either version 2 of the
12     License, or (at your option) any later version.  See the file
13     COPYING included with this distribution for more information.
14 */
15 
16 #ifndef RG_SCAVENGER_H
17 #define RG_SCAVENGER_H
18 
19 #include <vector>
20 #include <list>
21 #include <sys/time.h>
22 #include <pthread.h>
23 #include <iostream>
24 
25 namespace Rosegarden
26 {
27 
28 /**
29  * A very simple class that facilitates running things like plugins
30  * without locking, by collecting unwanted objects and deleting them
31  * after a delay so as to be sure nobody's in the middle of using
JackCaptureClient(const char * captureClientName,int fs)32  * them.  Requires scavenge() to be called regularly from a non-RT
33  * thread.
34  *
35  * This is currently not at all suitable for large numbers of objects
36  * -- it's just a quick hack for use with things like plugins.
37  */
38 
39 template <typename T>
40 class Scavenger
41 {
42 public:
43     Scavenger(int sec = 2, int defaultObjectListSize = 200);
44     ~Scavenger();
45 
46     /**
47      * Call from an RT thread etc., to pass ownership of t to us for
48      * later disposal.  Only one thread should be calling this on any
49      * given scavenger.
50      *
51      * This is only lock-free so long as a slot is available in the
52      * object list; otherwise it takes a lock and allocates memory.
53      * Scavengers should always be used with an object list size
54      * sufficient to ensure that enough slots are always available in
55      * normal use.
56      */
57     void claim(T *t);
58 
59     /**
60      * Call from a non-RT thread.
61      * Only one thread should be calling this on any given scavenger.
62      */
63     void scavenge();
64 
65 protected:
66     typedef std::pair<T *, int> ObjectTimePair;
67     typedef std::vector<ObjectTimePair> ObjectTimeList;
68     ObjectTimeList m_objects;
69     int m_sec;
70 
71     typedef std::list<T *> ObjectList;
72     ObjectList m_excess;
73     int m_lastExcess;
74     pthread_mutex_t m_excessMutex;
75     void pushExcess(T *);
76     void clearExcess(int);
77 
78     unsigned int m_claimed;
79     unsigned int m_scavenged;
80 };
81 
82 /**
~JackCaptureClient()83  * A wrapper to permit arrays to be scavenged.
84  */
85 
86 template <typename T>
87 class ScavengerArrayWrapper
88 {
89 public:
90     ScavengerArrayWrapper(T *array) : m_array(array) { }
91     ~ScavengerArrayWrapper() { delete[] m_array; }
92 
93 private:
94     T *m_array;
95 };
96 
97 
98 template <typename T>
99 Scavenger<T>::Scavenger(int sec, int defaultObjectListSize) :
100     m_objects(ObjectTimeList(defaultObjectListSize)),
101     m_sec(sec),
102     m_lastExcess(0),
103     m_claimed(0),
104     m_scavenged(0)
105 {
106     pthread_mutex_init(&m_excessMutex, nullptr);
107 }
108 
109 template <typename T>
110 Scavenger<T>::~Scavenger()
111 {
112     if (m_scavenged < m_claimed) {
113         for (size_t i = 0; i < m_objects.size(); ++i) {
114             ObjectTimePair &pair = m_objects[i];
115             if (pair.first != nullptr) {
116                 delete pair.first;
117                 pair.first = nullptr;
118                 ++m_scavenged;
119             }
120         }
121     }
122 
123     clearExcess(0);
getPorts()124 
125     pthread_mutex_destroy(&m_excessMutex);
126 }
127 
128 template <typename T>
129 void
getCapturePortName()130 Scavenger<T>::claim(T *t)
131 {
132     struct timeval tv;
133     (void)gettimeofday(&tv, nullptr);
134     int sec = tv.tv_sec;
135 
136     for (size_t i = 0; i < m_objects.size(); ++i) {
137         ObjectTimePair &pair = m_objects[i];
138         if (pair.first == nullptr) {
139             pair.second = sec;
140             pair.first = t;
141             ++m_claimed;
142             return;
143         }
144     }
145 
146     std::cerr << "WARNING: Scavenger::claim(" << t << "): run out of slots, "
147               << "using non-RT-safe method" << std::endl;
148     pushExcess(t);
149 }
150 
151 template <typename T>
152 void
153 Scavenger<T>::scavenge()
154 {
155     if (m_scavenged >= m_claimed) return;
156 
157     struct timeval tv;
158     (void)gettimeofday(&tv, nullptr);
159     int sec = tv.tv_sec;
160 
161     for (size_t i = 0; i < m_objects.size(); ++i) {
162         ObjectTimePair &pair = m_objects[i];
163         if (pair.first != 0 && pair.second + m_sec < sec) {
164             delete pair.first;
165             pair.first = nullptr;
166             ++m_scavenged;
167         }
168     }
169 
170     if (sec > m_lastExcess + m_sec) {
171         clearExcess(sec);
172     }
173 }
174 
175 template <typename T>
176 void
177 Scavenger<T>::pushExcess(T *t)
178 {
179     pthread_mutex_lock(&m_excessMutex);
180     m_excess.push_back(t);
181     struct timeval tv;
182     (void)gettimeofday(&tv, nullptr);
183     m_lastExcess = tv.tv_sec;
184     pthread_mutex_unlock(&m_excessMutex);
185 }
186 
187 template <typename T>
188 void
189 Scavenger<T>::clearExcess(int sec)
190 {
191     pthread_mutex_lock(&m_excessMutex);
192     for (typename ObjectList::iterator i = m_excess.begin();
193          i != m_excess.end(); ++i) {
194         delete *i;
195     }
196     m_excess.clear();
process(jack_nframes_t nframes,void * arg)197     m_lastExcess = sec;
198     pthread_mutex_unlock(&m_excessMutex);
199 }
200 
201 }
202 
203 #endif
204