Ticket #1660: asyncdb.patch

File asyncdb.patch, 15.0 KB (added by ajlill@…, 6 years ago)

Updated speedup.patch to 0.20 - this is just the changes to make the programinfo class use an asynchronous db thread, since it make the realtime fixes superflous

Line 
1Index: libs/libmythtv/NuppelVideoPlayer.cpp
2===================================================================
3--- libs/libmythtv/NuppelVideoPlayer.cpp        (revision 11414)
4+++ libs/libmythtv/NuppelVideoPlayer.cpp        (working copy)
5@@ -5242,11 +5242,11 @@
6     using_null_videoout = true;
7 
8     // clear out any existing seektables
9-    m_playbackinfo->ClearPositionMap(MARK_KEYFRAME);
10-    m_playbackinfo->ClearPositionMap(MARK_GOP_START);
11-    m_playbackinfo->ClearPositionMap(MARK_GOP_BYFRAME);
12+    m_playbackinfo->ClearPositionMap(MARK_KEYFRAME,true);
13+    m_playbackinfo->ClearPositionMap(MARK_GOP_START,true);
14+    m_playbackinfo->ClearPositionMap(MARK_GOP_BYFRAME,true);
15 
16     if (OpenFile() < 0)
17         return(0);
18Index: libs/libmythtv/programinfo.h
19===================================================================
20--- libs/libmythtv/programinfo.h        (revision 11414)
21+++ libs/libmythtv/programinfo.h        (working copy)
22@@ -220,11 +220,11 @@
23     // Keyframe positions Map
24     void GetPositionMap(frm_pos_map_t &, int type) const;
25     void ClearPositionMap(int type) const;
26+    void ClearPositionMap(int type, bool sync) const;
27     void SetPositionMap(frm_pos_map_t &, int type,
28                         long long min_frm = -1, long long max_frm = -1) const;
29-    void SetPositionMapDelta(frm_pos_map_t &, int type) const;
30+    void SetPositionMapDelta(frm_pos_map_t &, int type);
31 
32-
33     // GUI stuff
34     void showDetails(void) const;
35     void EditRecording(void);
36@@ -322,8 +322,9 @@
37   private:
38     bool ignoreBookmark;
39     mutable class ScheduledRecording* record;
40+   
41+    QString inUseForWhat;
42 
43-    QString inUseForWhat;
44 };
45 
46 /** \class ProgramList
47Index: libs/libmythtv/programinfo.cpp
48===================================================================
49--- libs/libmythtv/programinfo.cpp      (revision 11414)
50+++ libs/libmythtv/programinfo.cpp      (working copy)
51@@ -19,6 +19,7 @@
52 #include "remoteutil.h"
53 #include "jobqueue.h"
54 #include "mythdbcon.h"
55+#include "asyncdb.h"
56 
57 using namespace std;
58 
59@@ -1479,12 +1480,15 @@
60     if (playbackHost == "")
61         playbackHost = m_hostname;
62 
63+    // Get the original path it was recorded on
64     tmpURL = GetRecordFilename(gContext->GetSettingOnHost("RecordFilePrefix",
65                                                           hostname));
66 
67+    // if we are playing back on the recording host, just return it
68     if (playbackHost == hostname)
69         return tmpURL;
70 
71+    // else if we are playing back on this host, check if the path is accessable, if so, use it
72     if (playbackHost == m_hostname)
73     {
74         QFile checkFile(tmpURL);
75@@ -1500,6 +1504,7 @@
76             return tmpURL;
77     }
78 
79+    // else contruct a url to use myth file transfer protocol
80     tmpURL = QString("myth://") +
81              gContext->GetSettingOnHost("BackendServerIP", hostname) + ":" +
82              gContext->GetSettingOnHost("BackendServerPort", hostname) + "/" +
83@@ -1757,17 +1762,14 @@
84 {
85     filesize = fsize;
86 
87-    MSqlQuery query(MSqlQuery::InitCon());
88-    query.prepare("UPDATE recorded SET filesize = :FILESIZE"
89-                  " WHERE chanid = :CHANID"
90-                  " AND starttime = :STARTTIME ;");
91-    query.bindValue(":FILESIZE", longLongToString(fsize));
92-    query.bindValue(":CHANID", chanid);
93-    query.bindValue(":STARTTIME", recstartts);
94+    QString query = QString("UPDATE recorded SET filesize = %1"
95+                           " WHERE chanid = %2"
96+                           " AND starttime = \"%3\" ;")
97+      .arg(longLongToString(fsize))
98+      .arg(chanid)
99+      .arg(recstartts.toString("yyyyMMddhhmmss"));
100     
101-    if (!query.exec() || !query.isActive())
102-        MythContext::DBError("File size update",
103-                             query);
104+    gAsyncDB->AddCommand(query);
105 }
106 
107 /** \fn ProgramInfo::GetFilesize(void)
108@@ -2388,6 +2390,30 @@
109 
110 void ProgramInfo::ClearPositionMap(int type) const
111 {
112+    QString query;
113
114+    if (isVideo)
115+    {
116+        query = QString("DELETE FROM filemarkup"
117+                       " WHERE filename = \"%1\"")
118+        .arg(pathname);
119+    }
120+    else
121+    {
122+        query = QString("DELETE FROM recordedseek"
123+                      " WHERE chanid = %1"
124+                      " AND starttime = \"%2\"")
125+        .arg(chanid)
126+        .arg(recstartts.toString("yyyyMMddhhmmss"));
127+    }
128+    query += QString(" AND type = %1 ;").arg(type);
129+                               
130+    /* Hand it to the async thread */
131+    gAsyncDB->AddCommand(query);
132+}
133+
134+void ProgramInfo::ClearPositionMap(int type, bool sync) const
135+{
136     MSqlQuery query(MSqlQuery::InitCon());
137   
138     if (isVideo)
139@@ -2505,11 +2531,30 @@
140 }
141 
142 void ProgramInfo::SetPositionMapDelta(frm_pos_map_t &posMap,
143-                                      int type) const
144+                                      int type)
145 {
146     QMap<long long, long long>::Iterator i;
147-    MSqlQuery query(MSqlQuery::InitCon());
148+    QString insert;
149 
150+    if( posMap.isEmpty() )
151+      return;
152+
153+    if (isVideo)
154+      {
155+       insert = QString("INSERT INTO filemarkup"
156+                        " (filename, mark, type, offset)"
157+                        " VALUES");
158+      }
159+    else
160+      {
161+       insert = QString("INSERT INTO recordedseek"
162+                        " (chanid, starttime, mark, type, offset)"
163+                        " VALUES");
164+      }
165+    QString sep = " ";
166+
167+    /* Assemble these these updates into a single multi-row insert
168+     * statement -- much more efficient */
169     for (i = posMap.begin(); i != posMap.end(); ++i)
170     {
171         long long frame = i.key();
172@@ -2524,30 +2569,22 @@
173         QString offset_str = tempc;
174 
175         if (isVideo)
176-        {
177-            query.prepare("INSERT INTO filemarkup"
178-                          " (filename, mark, type, offset)"
179-                          " VALUES"
180-                          " ( :PATH , :MARK , :TYPE , :OFFSET );");
181-            query.bindValue(":PATH", pathname);
182-        }
183-        else
184-        {
185-            query.prepare("INSERT INTO recordedseek"
186-                          " (chanid, starttime, mark, type, offset)"
187-                          " VALUES"
188-                          " ( :CHANID , :STARTTIME , :MARK , :TYPE , :OFFSET );");
189-            query.bindValue(":CHANID", chanid);
190-            query.bindValue(":STARTTIME", recstartts);
191-        }
192-        query.bindValue(":MARK", frame_str);
193-        query.bindValue(":TYPE", type);
194-        query.bindValue(":OFFSET", offset_str);
195-       
196-        if (!query.exec() || !query.isActive())
197-            MythContext::DBError("delta position map insert",
198-                                 query);
199-    }
200+         {
201+           insert += sep + QString("( \"%1\", %2, %3, %4 )")
202+             .arg(pathname).arg(i.key()).arg(type).arg(i.data());
203+         }
204+          else
205+         {
206+           insert += sep + QString("( %1 , \"%2\" , %3 , %4 , %5 )")
207+             .arg(chanid).arg(recstartts.toString("yyyyMMddhhmmss"))
208+             .arg(i.key()).arg(type).arg(i.data());
209+         }
210+       sep = ",";
211+      }
212+
213+    insert += QString(";");
214+    /* Hand it to the async thread */
215+    gAsyncDB->AddCommand(insert);
216 }
217 
218 /** \fn ProgramInfo::ReactivateRecording(void)
219Index: libs/libmyth/exitcodes.h
220===================================================================
221--- libs/libmyth/exitcodes.h    (revision 11414)
222+++ libs/libmyth/exitcodes.h    (working copy)
223@@ -57,6 +57,7 @@
224 #define BACKEND_BUGGY_EXIT_NO_CAP_CARD            GENERIC_EXIT_START-11
225 #define BACKEND_BUGGY_EXIT_NO_CHAN_DATA           GENERIC_EXIT_START-12
226 #define BACKEND_EXIT_START                        GENERIC_EXIT_START-12
227+#define BACKEND_EXIT_ASYNCDB_ERROR               GENERIC_EXIT_START-13
228 
229 // mythtranscode
230 #define TRANSCODE_EXIT_OK                         GENERIC_EXIT_OK
231Index: libs/libmyth/libmyth.pro
232===================================================================
233--- libs/libmyth/libmyth.pro    (revision 11414)
234+++ libs/libmyth/libmyth.pro    (working copy)
235@@ -20,6 +20,7 @@
236 HEADERS += langsettings.h audiooutputnull.h mythsocket.h
237 HEADERS += DisplayResScreen.h util-x11.h mythdeque.h qmdcodec.h
238 HEADERS += exitcodes.h virtualkeyboard.h mythobservable.h mythevent.h
239+HEADERS += asyncdb.h
240 
241 SOURCES += dialogbox.cpp lcddevice.cpp mythcontext.cpp mythwidgets.cpp
242 SOURCES += oldsettings.cpp remotefile.cpp settings.cpp
243@@ -32,6 +33,7 @@
244 SOURCES += langsettings.cpp mythdbcon.cpp audiooutputnull.cpp
245 SOURCES += DisplayResScreen.cpp util-x11.cpp qmdcodec.cpp
246 SOURCES += virtualkeyboard.cpp mythobservable.cpp mythsocket.cpp
247+SOURCES += asyncdb.cpp
248 
249 INCLUDEPATH += ../libmythsamplerate ../libmythsoundtouch ../.. ../
250 DEPENDPATH += ../libmythsamplerate ../libmythsoundtouch ../ ../libmythui
251@@ -58,6 +60,7 @@
252 inc.files += visual.h volumebase.h output.h langsettings.h qmdcodec.h
253 inc.files += exitcodes.h mythconfig.h mythconfig.mak virtualkeyboard.h
254 inc.files += mythevent.h mythobservable.h mythsocket.h
255+inc.files += asyncdb.h
256 
257 using_oss {
258     DEFINES += USING_OSS
259Index: libs/libmyth/asyncdb.h
260===================================================================
261--- libs/libmyth/asyncdb.h      (revision 0)
262+++ libs/libmyth/asyncdb.h      (revision 0)
263@@ -0,0 +1,43 @@
264+#ifndef ASYNCDB_H_
265+#define ASYNCDB_H_
266+
267+// ANSI C headers
268+#include <cstdio>
269+#include <cstdlib>
270+#include <cerrno>
271+
272+#include "mythdbcon.h"
273+
274+#include <qstring.h>
275+
276+using namespace std;
277+
278+typedef struct adb_cmd_list {
279+    struct adb_cmd_list *next;
280+    QString cmd;
281+} adb_cmd_list_t;
282+
283+class AsyncDB
284+{
285+ public:
286+    AsyncDB(void);
287+    ~AsyncDB();
288+    bool Init(void);
289+    void AddCommand(QString); 
290+
291+ private:
292+    pthread_t thread;
293+    bool threadRunning;
294+    QStringList list;
295+//    adb_cmd_list_t *list;
296+    QMutex listLock;
297+    MSqlQuery query;
298+    bool ListRunner(adb_cmd_list_t *);
299+
300+ protected:
301+    static void *StartThread(void *);
302+    void Worker(void);
303+};
304+
305+extern AsyncDB *gAsyncDB;
306+#endif
307Index: libs/libmyth/asyncdb.cpp
308===================================================================
309--- libs/libmyth/asyncdb.cpp    (revision 0)
310+++ libs/libmyth/asyncdb.cpp    (revision 0)
311@@ -0,0 +1,162 @@
312+#include "asyncdb.h"
313+#include <unistd.h>
314+#include <sys/time.h>
315+#include <sys/resource.h>
316+#include <sys/mman.h>
317+
318+AsyncDB *gAsyncDB = NULL;
319+#define ADB QString("AsyncDB: ")
320+
321+/** \class AsyncDB
322+ *  \brief This class supports asynchronous database inserts.
323+ *
324+ *   This class allows us to toss those database queries for which
325+ *   we do not require values or status to be run by a separate thread.
326+ *   This helps the enocder threads to keep up with the datastream.
327+ */
328+
329+/** \fn AsyncDB
330+ *  \brief Initialize the class
331+ */
332+AsyncDB::AsyncDB(void):
333+  query(MSqlQuery::InitCon()), threadRunning(false)
334+{
335+  list.clear();
336+}
337+
338+/** \fn Init()
339+ *  \brief Starts the thread
340+ *
341+ *   Create a thread and return false if this fails
342+ */
343+bool AsyncDB::Init(void)
344+{
345+  int rv;
346+  if(  ( rv = pthread_create(&thread, NULL, StartThread, this) ) ) {
347+    VERBOSE(VB_IMPORTANT, ADB + QString("Can't start thread"));
348+    return false;
349+  }
350+  threadRunning = true;
351+  return(true);
352+}
353+
354+/** \fn StartThread(AsyncDB *)
355+ *  \brief Runs the worker function
356+ *
357+ *   Drop the priority on this thread and invoke the worker function
358+ */
359+void *AsyncDB::StartThread(void *wotzit)
360+{
361+  AsyncDB *pi = (AsyncDB *)wotzit;
362+  VERBOSE(VB_IMPORTANT,QString("Starting async db thread"));
363+  // Loser priority, to avoid problems with recordings.
364+  setpriority(PRIO_PROCESS, 0, 3);
365+  pi->Worker();
366+  return(NULL);
367+}
368+
369+/** \fn AddCommand(QString)
370+ *  \brief Adds a database command to the list
371+ *
372+ *  \param QString   The sql command
373+ */
374+void AsyncDB::AddCommand(QString cmd)
375+{
376+  listLock.lock();
377+  list.append(cmd);
378+  listLock.unlock();
379+//  adb_cmd_list_t *item = new adb_cmd_list_t;
380+//  item->cmd = cmd;
381+//  listLock.lock();
382+//  item->next = list;
383+//  list = item;
384+//  listLock.unlock();
385+}
386+
387+/** \fn ~AsyncDB
388+ *  \brief Shut down the thread
389+ *
390+ *   Add a "done" command to the list to make the thread
391+ *   shutdown and reap it.
392+ */
393+AsyncDB::~AsyncDB(void)
394+{
395+  AddCommand(QString("done"));
396+  pthread_join(thread, NULL);
397+  VERBOSE(VB_IMPORTANT,QString("Ending async db thread"));
398+}
399+
400+/** \fn Worker()
401+ *  \brief Run the lists of commands
402+ *
403+ *   Swap the list for an empty one and call ListRunner
404+ *   to execute the commands.
405+ */
406+void AsyncDB::Worker(void)
407+{
408+  bool done = false;
409+
410+  while( ! done ) {
411+    if( list.empty() ) {
412+      sleep(1);
413+    } else {
414+      listLock.lock();
415+      QStringList mylist = list;
416+      list.clear();
417+      listLock.unlock();
418+      for ( QStringList::Iterator it = mylist.begin(); it != mylist.end(); ++it ) {
419+       if( *it == QString("done") ) {
420+         done = true;
421+         break;
422+       }
423+       query.prepare(*it);
424+       if (!query.exec() || !query.isActive())
425+         MythContext::DBError("delta position map insert",
426+                              query);
427+      }
428+      mylist.clear();
429+    }
430+  }
431+//  while( ! done ) {
432+//    if( list == NULL ) {
433+//      sleep(1);
434+//    } else {
435+//      listLock.lock();
436+//      adb_cmd_list_t *mylist = list;
437+//      list = NULL;
438+//      listLock.unlock();
439+//      done = ListRunner(mylist);
440+//    }
441+//  }
442+}
443+
444+/** \fn ListRunner( adb_cmd_list_t * )
445+ *  \brief Execute the accumulated commands
446+ *
447+ *   Standard recursive routine. Shoot down to the end of the
448+ *   list and as we return back execute the commands and clean
449+ *   up the objects. This maintains order, since the newest
450+ *   commands are at the head of the list.
451+ *
452+ *   If we find the command "done" return true.
453+ *
454+ *  \param adb_cmd_list_t*  The rest of the list
455+ */
456+bool AsyncDB::ListRunner( adb_cmd_list_t *list ) {
457+  bool done = false;
458+  if( list != NULL ) {
459+    done = ListRunner(list->next);
460+    if( list->cmd == QString("done") ) {
461+         done = true;
462+    } else {
463+      query.prepare(list->cmd);
464+      if (!query.exec() || !query.isActive())
465+       MythContext::DBError("async command failed",
466+                            query);
467+    }
468+    delete list->cmd;
469+    delete list;
470+  }
471+  return( done );
472+}
473+
474Index: programs/mythtranscode/main.cpp
475===================================================================
476--- programs/mythtranscode/main.cpp     (revision 11414)
477+++ programs/mythtranscode/main.cpp     (working copy)
478@@ -540,8 +545,8 @@
479 {
480     if (pginfo && ! mapfile)
481     {
482-        pginfo->ClearPositionMap(MARK_KEYFRAME);
483-        pginfo->ClearPositionMap(MARK_GOP_START);
484+        pginfo->ClearPositionMap(MARK_KEYFRAME, true);
485+        pginfo->ClearPositionMap(MARK_GOP_START, true);
486         pginfo->SetPositionMap(posMap, MARK_GOP_BYFRAME);
487     }
488     else if (mapfile)
489Index: programs/mythbackend/main.cpp
490===================================================================
491--- programs/mythbackend/main.cpp       (revision 11414)
492+++ programs/mythbackend/main.cpp       (working copy)
493@@ -35,6 +35,7 @@
494 #include "libmythtv/dbcheck.h"
495 #include "libmythtv/jobqueue.h"
496 #include "libmythupnp/upnp.h"
497+#include "libmyth/asyncdb.h"
498 
499 #include "upnpcdstv.h"
500 #include "upnpcdsmusic.h"
501@@ -202,6 +203,7 @@
502 
503 void cleanup(void)
504 {
505+    delete gAsyncDB;
506     delete gContext;
507 
508     if (sched)
509@@ -497,6 +499,13 @@
510     }   
511     gContext->ActivateSettingsCache(true);
512 
513+    gAsyncDB = new AsyncDB();
514+    if( gAsyncDB->Init() == false )
515+      {
516+       VERBOSE(VB_IMPORTANT, "Couldn't start async database thread");
517+       return BACKEND_EXIT_ASYNCDB_ERROR;
518+      }
519+
520     if (printsched || testsched)
521     {
522         gContext->SetBackend(false);