root/trunk/libffado/src/libieee1394/IsoHandlerManager.h

Revision 1985, 14.7 kB (checked in by jwoithe, 13 years ago)

Fix double-free on exit under the new firewire stack. It seems that with the new kernel firewire stack, raw1394_destroy_handle() can take upwards of 20 milliseconds(!) to return. Therefore the IsoHandler?'s disable() call invoked by the IsoTask? (FW_ISORCV or FW_ISOXMT) may not have completed before the "jackd" thread calls ~IsoHandler?(). ~IsoHandler?() thus infers that the handler is still running and calls disable() itself. The practical upshot is that raw1394_destroy_handle() gets called on the same object twice, and a double-free results.

The fix I've implemented is a touch crude, but it appears to work. A mutex is introduced to track the progress of disable(), and this is checked by ~IsoHandler?() before the state of the handler is tested. Any in-progress disable() call is allowed to complete before ~IsoHandler?() tests the state. This prevents the second call of raw1394_destroy_handle() and therefore the double-free cannot occur.

Perhaps as a result of the delays caused by raw1394_destroy_handle(), it seems the handler list can be altered by other threads while updateShadowMapHelper() (called by the IsoTask? threads) is running. A crude test has been added to this function to prevent out-of-range exceptions in most cases.

None of this is particularly elegant but it should work around the double-free issue for the moment. The correct approach is to work out precisely why these concurrency issues are occuring and fix them. However, given that all this will be obsoleted by the in-kernel streaming work at some point in the future, it's arguable that the solution in this patch is sufficient in practice.

Line 
1 /*
2  * Copyright (C) 2005-2008 by Pieter Palmers
3  *
4  * This file is part of FFADO
5  * FFADO = Free Firewire (pro-)audio drivers for linux
6  *
7  * FFADO is based upon FreeBoB.
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 2 of the License, or
12  * (at your option) version 3 of the License.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  */
23
24 #ifndef __FFADO_ISOHANDLERMANAGER__
25 #define __FFADO_ISOHANDLERMANAGER__
26
27 #include "config.h"
28
29 #include "debugmodule/debugmodule.h"
30
31 #include "libutil/Thread.h"
32
33 #include <sys/poll.h>
34 #include <errno.h>
35 #include <vector>
36 #include <semaphore.h>
37
38 class Ieee1394Service;
39 //class IsoHandler;
40 //enum IsoHandler::EHandlerType;
41
42 namespace Streaming {
43     class StreamProcessor;
44     typedef std::vector<StreamProcessor *> StreamProcessorVector;
45     typedef std::vector<StreamProcessor *>::iterator StreamProcessorVectorIterator;
46 }
47
48 /*!
49 \brief The ISO Handler management class
50
51  This class manages the use of ISO handlers by ISO streams.
52  You can register an Streaming::StreamProcessor with an IsoHandlerManager. This
53  manager will assign an IsoHandler to the stream. If nescessary
54  the manager allocates a new handler. If there is already a handler
55  that can handle the Streaming::StreamProcessor (e.g. in case of multichannel receive),
56  it can be assigned.
57
58 */
59
60 class IsoHandlerManager
61 {
62     friend class IsoTask;
63
64 ////
65 /*!
66     \brief The Base Class for ISO Handlers
67
68     These classes perform the actual ISO communication through libraw1394.
69     They are different from Streaming::StreamProcessors because one handler can provide multiple
70     streams with packets in case of ISO multichannel receive.
71
72  */
73
74     class IsoHandler
75     {
76         public:
77             enum EHandlerType {
78                 eHT_Receive,
79                 eHT_Transmit
80             };
81             IsoHandler(IsoHandlerManager& manager, enum EHandlerType t);
82             IsoHandler(IsoHandlerManager& manager, enum EHandlerType t,
83                        unsigned int buf_packets, unsigned int max_packet_size, int irq);
84             IsoHandler(IsoHandlerManager& manager, enum EHandlerType t,
85                        unsigned int buf_packets, unsigned int max_packet_size, int irq, enum raw1394_iso_speed speed);
86             ~IsoHandler();
87
88             private: // the ISO callback interface
89                 static enum raw1394_iso_disposition
90                         iso_receive_handler(raw1394handle_t handle, unsigned char *data,
91                                             unsigned int length, unsigned char channel,
92                                             unsigned char tag, unsigned char sy, unsigned int cycle,
93                                             unsigned int dropped);
94
95                 enum raw1394_iso_disposition
96                         putPacket(unsigned char *data, unsigned int length,
97                                   unsigned char channel, unsigned char tag, unsigned char sy,
98                                   unsigned int cycle, unsigned int dropped);
99
100                 static enum raw1394_iso_disposition iso_transmit_handler(raw1394handle_t handle,
101                         unsigned char *data, unsigned int *length,
102                         unsigned char *tag, unsigned char *sy,
103                         int cycle, unsigned int dropped);
104                 enum raw1394_iso_disposition
105                         getPacket(unsigned char *data, unsigned int *length,
106                                   unsigned char *tag, unsigned char *sy,
107                                   int cycle, unsigned int dropped, unsigned int skipped);
108
109         public:
110
111     /**
112          * Iterate the handler, transporting ISO packets to the client(s)
113          * @return true if success
114      */
115             bool iterate();
116
117     /**
118              * Iterate the handler, transporting ISO packets to the client(s)
119              * @param  ctr_now the CTR time at which the iterate call is done.
120              * @return true if success
121      */
122             bool iterate(uint32_t ctr_now);
123
124             int getFileDescriptor() { return raw1394_get_fd(m_handle);};
125
126             bool init();
127             void setVerboseLevel(int l);
128
129     // the enable/disable functions should only be used from within the loop that iterates()
130     // but not from within the iterate callback. use the requestEnable / requestDisable functions
131     // for that
132             bool enable() {return enable(-1);};
133             bool enable(int cycle);
134             bool disable();
135
136     // functions to request enable or disable at the next opportunity
137             bool requestEnable(int cycle = -1);
138             bool requestDisable();
139
140             // Manually set the start cycle for the iso handler
141             void setIsoStartCycle(signed int cycle = -1);
142
143     /**
144              * updates the internal state if required
145      */
146             void updateState();
147
148             enum EHandlerType getType() {return m_type;};
149             const char *getTypeString() {return eHTToString(m_type); };
150
151     // pretty printing
152             const char *eHTToString(enum EHandlerType);
153
154             bool isEnabled()
155             {return m_State == eHS_Running;};
156
157     // no setter functions, because those would require a re-init
158             unsigned int getMaxPacketSize() { return m_max_packet_size;};
159             unsigned int getNbBuffers() { return m_buf_packets;};
160             int getIrqInterval() { return m_irq_interval;};
161
162             void dumpInfo();
163
164             bool inUse() {return (m_Client != 0) ;};
165             bool isStreamRegistered(Streaming::StreamProcessor *s) {return (m_Client == s);};
166
167             bool registerStream(Streaming::StreamProcessor *);
168             bool unregisterStream(Streaming::StreamProcessor *);
169
170             bool canIterateClient(); // FIXME: implement with functor
171
172
173     /**
174              * @brief get last cycle number seen by handler
175              * @return cycle number
176      */
177             int getLastCycle() {return m_last_cycle;};
178
179     /**
180              * @brief returns the CTR value saved at the last iterate() call
181              * @return CTR value saved at last iterate() call
182      */
183             uint32_t getLastIterateTime() {return m_last_now;};
184
185     /**
186              * @brief returns the CTR value saved at the last iterate handler call
187              * @return CTR value saved at last iterate handler call
188      */
189             uint32_t getLastPacketTime() {return m_last_packet_handled_at;};
190
191     /**
192              * @brief set iso receive mode. doesn't have any effect if the stream is running
193              * @param m receive mode
194      */
195             void setReceiveMode(enum raw1394_iso_dma_recv_mode m)
196             {m_receive_mode = m;}
197
198             void notifyOfDeath();
199             bool handleBusReset();
200
201         private:
202             IsoHandlerManager& m_manager;
203             enum EHandlerType m_type;
204             raw1394handle_t m_handle;
205             unsigned int    m_buf_packets;
206             unsigned int    m_max_packet_size;
207             int             m_irq_interval;
208             int             m_last_cycle;
209             uint32_t        m_last_now;
210             uint32_t        m_last_packet_handled_at;
211             enum raw1394_iso_dma_recv_mode m_receive_mode;
212
213             Streaming::StreamProcessor *m_Client; // FIXME: implement with functors
214
215             enum raw1394_iso_speed m_speed;
216
217     // the state machine
218             enum EHandlerStates {
219                 eHS_Stopped,
220                 eHS_Running,
221                 eHS_Error,
222             };
223             enum EHandlerStates m_State;
224             enum EHandlerStates m_NextState;
225             int m_switch_on_cycle;
226
227             pthread_mutex_t m_disable_lock;
228
229         public:
230             unsigned int    m_packets;
231 #ifdef DEBUG
232             unsigned int    m_dropped;
233             unsigned int    m_skipped;
234             int             m_min_ahead;
235 #endif
236
237         protected:
238             DECLARE_DEBUG_MODULE;
239     };
240
241     typedef std::vector<IsoHandler *> IsoHandlerVector;
242     typedef std::vector<IsoHandler *>::iterator IsoHandlerVectorIterator;
243
244 ////
245    
246 // threads that will handle the packet framing
247 // one thread per direction, as a compromise for one per
248 // channel and one for all
249     class IsoTask : public Util::RunnableInterface
250     {
251         friend class IsoHandlerManager;
252         public:
253             IsoTask(IsoHandlerManager& manager, enum IsoHandler::EHandlerType);
254             virtual ~IsoTask();
255
256         private:
257             bool Init();
258             bool Execute();
259
260         /**
261              * @brief requests the thread to sync it's stream map with the manager
262          */
263             void requestShadowMapUpdate();
264             enum eActivityResult {
265                 eAR_Activity,
266                 eAR_Timeout,
267                 eAR_Interrupted,
268                 eAR_Error
269             };
270
271         /**
272              * @brief signals that something happened in one of the clients of this task
273          */
274             void signalActivity();
275         /**
276              * @brief wait until something happened in one of the clients of this task
277          */
278             enum eActivityResult waitForActivity();
279
280         /**
281              * @brief This should be called when a busreset has happened.
282          */
283             bool handleBusReset();
284
285             void setVerboseLevel(int i);
286
287         protected:
288             IsoHandlerManager& m_manager;
289
290         // the event request structure
291             int32_t request_update;
292
293         // static allocation due to RT constraints
294         // this is the map used by the actual thread
295         // it is a shadow of the m_StreamProcessors vector
296             struct pollfd   m_poll_fds_shadow[ISOHANDLERMANAGER_MAX_ISO_HANDLERS_PER_PORT];
297             IsoHandler *    m_IsoHandler_map_shadow[ISOHANDLERMANAGER_MAX_ISO_HANDLERS_PER_PORT];
298             unsigned int    m_poll_nfds_shadow;
299             IsoHandler *    m_SyncIsoHandler;
300
301         // updates the streams map
302             void updateShadowMapHelper();
303
304 #ifdef DEBUG
305             uint64_t m_last_loop_entry;
306             int m_successive_short_loops;
307 #endif
308
309             enum IsoHandler::EHandlerType m_handlerType;
310             bool m_running;
311             bool m_in_busreset;
312
313         // activity signaling
314             sem_t m_activity_semaphore;
315             long long int m_activity_wait_timeout_nsec;
316
317         // debug stuff
318             DECLARE_DEBUG_MODULE;
319     };
320    
321 //// the IsoHandlerManager itself
322     public:
323
324         IsoHandlerManager(Ieee1394Service& service);
325         IsoHandlerManager(Ieee1394Service& service, bool run_rt, int rt_prio);
326         virtual ~IsoHandlerManager();
327
328         bool setThreadParameters(bool rt, int priority);
329
330         void setVerboseLevel(int l); ///< set the verbose level
331
332         void dumpInfo(); ///< print some information about the manager to stdout/stderr
333         void dumpInfoForStream(Streaming::StreamProcessor *); ///< print some info about the stream's handler
334
335         bool registerStream(Streaming::StreamProcessor *); ///< register an iso stream with the manager
336         bool unregisterStream(Streaming::StreamProcessor *); ///< unregister an iso stream from the manager
337
338         bool startHandlers(); ///< start the managed ISO handlers
339         bool startHandlers(int cycle); ///< start the managed ISO handlers
340         bool stopHandlers(); ///< stop the managed ISO handlers
341
342         bool reset(); ///< reset the ISO manager and all streams
343         bool init();
344
345         /**
346          * @brief signals that something happened in one of the clients
347          */
348         void signalActivityTransmit();
349         void signalActivityReceive();
350
351         ///> disables the handler attached to the stream
352         bool stopHandlerForStream(Streaming::StreamProcessor *);
353         ///> starts the handler attached to the specific stream
354         bool startHandlerForStream(Streaming::StreamProcessor *);
355         ///> starts the handler attached to the specific stream on a specific cycle
356         bool startHandlerForStream(Streaming::StreamProcessor *, int cycle);
357
358         ///> Directly tells the handler attached to the stream to start on
359         ///> the given cycle regardless of what is passed to
360         ///> startHandlerForStream().
361         void setIsoStartCycleForStream(Streaming::StreamProcessor *stream, signed int cycle);
362
363         /**
364          * returns the latency of a wake-up for this stream.
365          * The latency is the time it takes for a packet is delivered to the
366          * stream after it has been received (was on the wire).
367          * expressed in cycles
368          */
369         int getPacketLatencyForStream(Streaming::StreamProcessor *);
370
371         /**
372          * Enables the isohandler manager to ignore missed packets.  This
373          * behaviour is needed by some interfaces which don't send empty
374          * placeholder packets when no data needs to be sent.
375          */
376         void setMissedCyclesOK(bool ok) { m_MissedCyclesOK = ok; };
377
378     private:
379         IsoHandler * getHandlerForStream(Streaming::StreamProcessor *stream);
380         void requestShadowMapUpdate();
381     public:
382         Ieee1394Service& get1394Service() {return m_service;};
383
384         /**
385          * This should be called when a busreset has happened.
386          */
387         bool handleBusReset();
388
389     // the state machine
390     private:
391         enum eHandlerStates {
392             E_Created,
393             E_Prepared,
394             E_Running,
395             E_Error
396         };
397
398         enum eHandlerStates m_State;
399         const char *eHSToString(enum eHandlerStates);
400
401     private:
402         Ieee1394Service&  m_service;
403         // note: there is a disctinction between streams and handlers
404         // because one handler can serve multiple streams (in case of
405         // multichannel receive)
406
407         // only streams are allowed to be registered externally.
408         // we allocate a handler if we need one, otherwise the stream
409         // is assigned to another handler
410
411         // the collection of handlers
412         IsoHandlerVector m_IsoHandlers;
413
414         bool registerHandler(IsoHandler *);
415         bool unregisterHandler(IsoHandler *);
416         void pruneHandlers();
417
418         // the collection of streams
419         Streaming::StreamProcessorVector m_StreamProcessors;
420
421         // handler thread/task
422         bool            m_realtime;
423         int             m_priority;
424         Util::Thread *  m_IsoThreadTransmit;
425         IsoTask *       m_IsoTaskTransmit;
426         Util::Thread *  m_IsoThreadReceive;
427         IsoTask *       m_IsoTaskReceive;
428
429         bool            m_MissedCyclesOK;
430
431         // debug stuff
432         DECLARE_DEBUG_MODULE;
433
434 };
435
436 #endif /* __FFADO_ISOHANDLERMANAGER__  */
437
438
439
Note: See TracBrowser for help on using the browser.