Ticket #1660: asyncdb.13130.patch

File asyncdb.13130.patch, 13.3 KB (added by ajlill@…, 17 years ago)

Updated patch with retry logic

  • libs/libmythtv/NuppelVideoPlayer.cpp

     
    54295429    using_null_videoout = true;
    54305430
    54315431    // clear out any existing seektables
    5432     m_playbackinfo->ClearPositionMap(MARK_KEYFRAME);
    5433     m_playbackinfo->ClearPositionMap(MARK_GOP_START);
    5434     m_playbackinfo->ClearPositionMap(MARK_GOP_BYFRAME);
     5432    m_playbackinfo->ClearPositionMap(MARK_KEYFRAME,true);
     5433    m_playbackinfo->ClearPositionMap(MARK_GOP_START,true);
     5434    m_playbackinfo->ClearPositionMap(MARK_GOP_BYFRAME,true);
    54355435
    54365436    if (OpenFile() < 0)
    54375437        return(0);
  • libs/libmythtv/programinfo.h

     
    237237    // Keyframe positions Map
    238238    void GetPositionMap(frm_pos_map_t &, int type) const;
    239239    void ClearPositionMap(int type) const;
     240    void ClearPositionMap(int type, bool sync) const;
    240241    void SetPositionMap(frm_pos_map_t &, int type,
    241242                        long long min_frm = -1, long long max_frm = -1) const;
    242     void SetPositionMapDelta(frm_pos_map_t &, int type) const;
     243    void SetPositionMapDelta(frm_pos_map_t &, int type);
    243244
    244 
    245245    // GUI stuff
    246246    void showDetails(void) const;
    247247    void EditRecording(void);
     
    344344  private:
    345345    bool ignoreBookmark;
    346346    mutable class ScheduledRecording* record;
     347   
     348    QString inUseForWhat;
    347349
    348     QString inUseForWhat;
    349350};
    350351
    351352/** \class ProgramList
  • libs/libmythtv/programinfo.cpp

     
    1717#include "util.h"
    1818#include "mythcontext.h"
    1919#include "dialogbox.h"
     20#include "asyncdb.h"
    2021#include "remoteutil.h"
    2122#include "jobqueue.h"
    2223#include "mythdbcon.h"
     
    18281829{
    18291830    filesize = fsize;
    18301831
    1831     MSqlQuery query(MSqlQuery::InitCon());
    1832     query.prepare("UPDATE recorded SET filesize = :FILESIZE"
    1833                   " WHERE chanid = :CHANID"
    1834                   " AND starttime = :STARTTIME ;");
    1835     query.bindValue(":FILESIZE", longLongToString(fsize));
    1836     query.bindValue(":CHANID", chanid);
    1837     query.bindValue(":STARTTIME", recstartts);
     1832    QString query = QString("UPDATE recorded SET filesize = %1"
     1833                            " WHERE chanid = %2"
     1834                            " AND starttime = \"%3\" ;")
     1835      .arg(longLongToString(fsize))
     1836      .arg(chanid)
     1837      .arg(recstartts.toString("yyyyMMddhhmmss"));
    18381838   
    1839     if (!query.exec() || !query.isActive())
    1840         MythContext::DBError("File size update",
    1841                              query);
     1839    gAsyncDB->AddCommand(query);
    18421840}
    18431841
    18441842/** \fn ProgramInfo::GetFilesize(void)
     
    26052603
    26062604void ProgramInfo::ClearPositionMap(int type) const
    26072605{
     2606    QString query;
     2607 
     2608    if (isVideo)
     2609    {
     2610        query = QString("DELETE FROM filemarkup"
     2611                        " WHERE filename = \"%1\"")
     2612        .arg(pathname);
     2613    }
     2614    else
     2615    {
     2616        query = QString("DELETE FROM recordedseek"
     2617                      " WHERE chanid = %1"
     2618                      " AND starttime = \"%2\"")
     2619        .arg(chanid)
     2620        .arg(recstartts.toString("yyyyMMddhhmmss"));
     2621    }
     2622    query += QString(" AND type = %1 ;").arg(type);
     2623                               
     2624    /* Hand it to the async thread */
     2625    gAsyncDB->AddCommand(query);
     2626}
     2627
     2628void ProgramInfo::ClearPositionMap(int type, bool sync) const
     2629{
    26082630    MSqlQuery query(MSqlQuery::InitCon());
    26092631 
    26102632    if (isVideo)
     
    27102732}
    27112733
    27122734void ProgramInfo::SetPositionMapDelta(frm_pos_map_t &posMap,
    2713                                       int type) const
     2735                                      int type)
    27142736{
    27152737    QMap<long long, long long>::Iterator i;
    2716     MSqlQuery query(MSqlQuery::InitCon());
     2738    QString insert;
    27172739
     2740    if( posMap.isEmpty() )
     2741      return;
     2742
     2743    if (isVideo)
     2744      {
     2745        insert = QString("INSERT INTO filemarkup"
     2746                         " (filename, mark, type, offset)"
     2747                         " VALUES");
     2748      }
     2749    else
     2750      {
     2751        insert = QString("INSERT INTO recordedseek"
     2752                         " (chanid, starttime, mark, type, offset)"
     2753                         " VALUES");
     2754      }
     2755    QString sep = " ";
     2756
     2757    /* Assemble these these updates into a single multi-row insert
     2758     * statement -- much more efficient */
    27182759    for (i = posMap.begin(); i != posMap.end(); ++i)
    27192760    {
    27202761        long long frame = i.key();
    27212762        long long offset = i.data();
    27222763
    27232764        if (isVideo)
    2724         {
    2725             query.prepare("INSERT INTO filemarkup"
    2726                           " (filename, mark, type, offset)"
    2727                           " VALUES"
    2728                           " ( :PATH , :MARK , :TYPE , :OFFSET );");
    2729             query.bindValue(":PATH", pathname);
    2730         }
    2731         else
    2732         {
    2733             query.prepare("INSERT INTO recordedseek"
    2734                           " (chanid, starttime, mark, type, offset)"
    2735                           " VALUES"
    2736                           " ( :CHANID , :STARTTIME , :MARK , :TYPE , :OFFSET );");
    2737             query.bindValue(":CHANID", chanid);
    2738             query.bindValue(":STARTTIME", recstartts);
    2739         }
    2740         query.bindValue(":MARK", frame);
    2741         query.bindValue(":TYPE", type);
    2742         query.bindValue(":OFFSET", offset);
    2743        
    2744         if (!query.exec() || !query.isActive())
    2745             MythContext::DBError("delta position map insert",
    2746                                  query);
    2747     }
     2765          {
     2766            insert += sep + QString("( \"%1\", %2, %3, %4 )")
     2767              .arg(pathname).arg(frame).arg(type).arg(offset);
     2768          }
     2769          else
     2770          {
     2771            insert += sep + QString("( %1 , \"%2\" , %3 , %4 , %5 )")
     2772              .arg(chanid).arg(recstartts.toString("yyyyMMddhhmmss"))
     2773              .arg(frame).arg(type).arg(offset);
     2774          }
     2775        sep = ",";
     2776      }
     2777
     2778    insert += QString(";");
     2779    /* Hand it to the async thread */
     2780    gAsyncDB->AddCommand(insert);
    27482781}
    27492782
    27502783/** \fn ProgramInfo::ReactivateRecording(void)
  • libs/libmyth/exitcodes.h

     
    5757#define BACKEND_BUGGY_EXIT_NO_CAP_CARD            GENERIC_EXIT_START-11
    5858#define BACKEND_BUGGY_EXIT_NO_CHAN_DATA           GENERIC_EXIT_START-12
    5959#define BACKEND_EXIT_START                        GENERIC_EXIT_START-12
     60#define BACKEND_EXIT_ASYNCDB_ERROR                GENERIC_EXIT_START-13
    6061
    6162// mythtranscode
    6263#define TRANSCODE_EXIT_OK                         GENERIC_EXIT_OK
  • libs/libmyth/libmyth.pro

     
    2020HEADERS += langsettings.h audiooutputnull.h mythsocket.h
    2121HEADERS += DisplayResScreen.h util-x11.h mythdeque.h qmdcodec.h
    2222HEADERS += exitcodes.h virtualkeyboard.h mythobservable.h mythevent.h
     23HEADERS += asyncdb.h
    2324HEADERS += mythexp.h mythpluginapi.h
    2425
    2526SOURCES += dialogbox.cpp lcddevice.cpp mythcontext.cpp mythwidgets.cpp
     
    3334SOURCES += langsettings.cpp mythdbcon.cpp audiooutputnull.cpp
    3435SOURCES += DisplayResScreen.cpp util-x11.cpp qmdcodec.cpp
    3536SOURCES += virtualkeyboard.cpp mythobservable.cpp mythsocket.cpp
     37SOURCES += asyncdb.cpp
    3638
    3739INCLUDEPATH += ../libmythsamplerate ../libmythsoundtouch ../.. ../
    3840DEPENDPATH += ../libmythsamplerate ../libmythsoundtouch ../ ../libmythui
     
    5961inc.files += visual.h volumebase.h output.h langsettings.h qmdcodec.h
    6062inc.files += exitcodes.h mythconfig.h mythconfig.mak virtualkeyboard.h
    6163inc.files += mythevent.h mythobservable.h mythsocket.h
     64inc.files += asyncdb.h
    6265inc.files += mythexp.h mythpluginapi.h
    6366
    6467using_oss {
  • libs/libmyth/asyncdb.h

     
     1#ifndef ASYNCDB_H_
     2#define ASYNCDB_H_
     3
     4// ANSI C headers
     5#include <cstdio>
     6#include <cstdlib>
     7#include <cerrno>
     8
     9#include "mythdbcon.h"
     10
     11#include <qstring.h>
     12
     13using namespace std;
     14
     15class AsyncDB
     16{
     17 public:
     18    AsyncDB(void);
     19    ~AsyncDB();
     20    bool Init(void);
     21    void AddCommand(QString); 
     22
     23 private:
     24    pthread_t thread;
     25    QStringList list;
     26    QMutex listLock;
     27    int retry;
     28    bool threadRunning;
     29
     30 protected:
     31    static void *StartThread(void *);
     32    void Worker(void);
     33};
     34
     35extern AsyncDB *gAsyncDB;
     36#endif
  • libs/libmyth/asyncdb.cpp

     
     1#include "asyncdb.h"
     2#include <unistd.h>
     3#include <sys/time.h>
     4#include <sys/resource.h>
     5#include <sys/mman.h>
     6
     7AsyncDB *gAsyncDB = NULL;
     8#define ADB QString("AsyncDB: ")
     9
     10/** \class AsyncDB
     11 *  \brief This class supports asynchronous database inserts.
     12 *
     13 *   This class allows us to toss those database queries for which
     14 *   we do not require values or status to be run by a separate thread.
     15 *   This helps the enocder threads to keep up with the datastream.
     16 */
     17
     18/** \fn AsyncDB
     19 *  \brief Initialize the class
     20 */
     21AsyncDB::AsyncDB(void):
     22  retry(0), threadRunning(false)
     23{
     24  list.clear();
     25}
     26
     27/** \fn Init()
     28 *  \brief Starts the thread
     29 *
     30 *   Create a thread and return false if this fails
     31 */
     32bool AsyncDB::Init(void)
     33{
     34  int rv;
     35  if(  ( rv = pthread_create(&thread, NULL, StartThread, this) ) ) {
     36    VERBOSE(VB_IMPORTANT, ADB + QString("Can't start thread"));
     37    return false;
     38  }
     39  return(true);
     40}
     41
     42/** \fn StartThread(AsyncDB *)
     43 *  \brief Runs the worker function
     44 *
     45 *   Drop the priority on this thread and invoke the worker function
     46 */
     47void *AsyncDB::StartThread(void *wotzit)
     48{
     49  AsyncDB *pi = (AsyncDB *)wotzit;
     50  VERBOSE(VB_IMPORTANT,QString("Starting async db thread"));
     51  // Loser priority, to avoid problems with recordings.
     52  pi->threadRunning = true;
     53  setpriority(PRIO_PROCESS, 0, 3);
     54  do {
     55    pi->Worker();
     56  } while(pi->retry <= 1 );     // A second retry means we're hosed
     57  pi->threadRunning = false;
     58  return(NULL);
     59}
     60
     61/** \fn AddCommand(QString)
     62 *  \brief Adds a database command to the list
     63 *
     64 *  \param QString   The sql command
     65 */
     66void AsyncDB::AddCommand(QString cmd)
     67{
     68  if( threadRunning ) {
     69    listLock.lock();
     70    list.append(cmd);
     71    listLock.unlock();
     72  }
     73}
     74
     75/** \fn ~AsyncDB
     76 *  \brief Shut down the thread
     77 *
     78 *   Add a "done" command to the list to make the thread
     79 *   shutdown and reap it.
     80 */
     81AsyncDB::~AsyncDB(void)
     82{
     83  AddCommand(QString("done"));
     84  pthread_join(thread, NULL);
     85  VERBOSE(VB_IMPORTANT,QString("Ending async db thread"));
     86}
     87
     88/** \fn Worker()
     89 *  \brief Run the lists of commands
     90 *
     91 *   Swap the list for an empty one and call ListRunner
     92 *   to execute the commands.
     93 */
     94void AsyncDB::Worker(void)
     95{
     96  bool done = false;
     97  bool errored = false;
     98  MSqlQuery query(MSqlQuery::InitCon());
     99
     100  while( ! done && ! errored ) {
     101    if( list.empty() ) {
     102      sleep(1);
     103    } else {
     104      listLock.lock();
     105      QStringList mylist = list;
     106      list.clear();
     107      listLock.unlock();
     108      for ( QStringList::Iterator it = mylist.begin(); it != mylist.end(); ++it ) {
     109        if( *it == QString("done") ) {
     110          done = true;
     111          retry = 2;                    // Make calling function exit.
     112          break;
     113        }
     114        if( errored ) {
     115          /* If we experienece a database error, we stuff the commands back onto the list,
     116             and exit this function. We get called again which refreshes the DB connection.
     117             We count these retries, so we don't loop, but only do it once without some
     118             success */
     119          AddCommand(*it);
     120        } else {
     121          query.prepare(*it);
     122          if (!query.exec() || !query.isActive()) {
     123            MythContext::DBError("delta position map insert",
     124                                 query);
     125            retry++;
     126            errored = true;
     127            AddCommand(*it);
     128          } else
     129            retry = 0;
     130        }
     131      }
     132      mylist.clear();
     133    }
     134  }
     135}
     136
     137
  • programs/mythtranscode/main.cpp

     
    561561{
    562562    if (pginfo && ! mapfile)
    563563    {
    564         pginfo->ClearPositionMap(MARK_KEYFRAME);
    565         pginfo->ClearPositionMap(MARK_GOP_START);
     564        pginfo->ClearPositionMap(MARK_KEYFRAME, true);
     565        pginfo->ClearPositionMap(MARK_GOP_START, true);
    566566        pginfo->SetPositionMap(posMap, MARK_GOP_BYFRAME);
    567567    }
    568568    else if (mapfile)
  • programs/mythbackend/main.cpp

     
    3535#include "libmythtv/dbcheck.h"
    3636#include "libmythtv/jobqueue.h"
    3737#include "libmythtv/storagegroup.h"
     38#include "libmyth/asyncdb.h"
    3839
    3940#include "mediaserver.h"
    4041#include "httpstatus.h"
     
    199200
    200201void cleanup(void)
    201202{
     203    delete gAsyncDB;
    202204    delete gContext;
    203205
    204206    if (sched)
     
    518520    }   
    519521    gContext->ActivateSettingsCache(true);
    520522
     523    gAsyncDB = new AsyncDB();
     524    if( gAsyncDB->Init() == false )
     525      {
     526        VERBOSE(VB_IMPORTANT, "Couldn't start async database thread");
     527        return BACKEND_EXIT_ASYNCDB_ERROR;
     528      }
     529
    521530    if (printsched || testsched)
    522531    {
    523532        gContext->SetBackend(false);