Bug 105387 - kbuildsycoca can't recreate the ksycoca database after initial startup
Summary: kbuildsycoca can't recreate the ksycoca database after initial startup
Status: RESOLVED FIXED
Alias: None
Product: kdelibs
Classification: Frameworks and Libraries
Component: kded (show other bugs)
Version: unspecified
Platform: Compiled Sources Cygwin
: NOR normal
Target Milestone: ---
Assignee: Unassigned bugs mailing-list
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-05-10 07:21 UTC by devsk
Modified: 2015-09-19 23:04 UTC (History)
2 users (show)

See Also:
Latest Commit:
Version Fixed In:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description devsk 2005-05-10 07:21:40 UTC
Version:            (using KDE KDE 3.4.0)
Installed from:    Compiled From Sources
Compiler:          gcc 3.3.3 cygwin special
OS:                Cygwin

I get this error while updating or adding an entry in the menu using kmenuedit:

kbuildsycoca: ERROR writing database '/var/tmp/kdecache-sunil/ksycoca'!
kbuildsycoca: Disk full?

Basically this is what is going on inside of kbuildsycoca:

the problem with updating ksycoca database is coming from database.close() line in kbuildsycoca.cpp :

    if (!database.close()) 
    {  
      fprintf(stderr, "kbuildsycoca: ERROR writing database '%s'!\n",database.name().local8Bit().data());
      fprintf(stderr, "kbuildsycoca: Disk full?\n");
#ifdef KBUILDSYCOCA_GUI
      if (!silent)
        KMessageBox::error(0, i18n("Error writing database '%1'.\nCheck that the permissions are correct on the directory and the disk is not full.\n").arg(path.local8Bit().data()),i18n("KBuildSycoca"));
#endif
      return false;
    }

kdecore/ksavefile.cpp has the following piece of code where close() renames the temporary file to the ksycoca in /var/tmp/kdecache-<username>/. This fails because some process has opened that file READ_ONLY. cygwin/windows doesn't allow overwriting a file which is open in another process. It looks like it is kded daemon as one of processes which has it opened. But I am not sure how to ask these processes to close this file.

   if (mTempFile.close())
   {
      if (0==KDE_rename(QFile::encodeName(mTempFile.name()),QFile::encodeName(mFileName)))
         return true; // Success!
      mTempFile.setError(errno);
   }

this is where processes open the ksycoca database in kdecore/ksycoca.cpp:

   kdDebug(7011) << "Trying to open ksycoca from " << path << endl;
   QFile *database = new QFile(path);
   bool bOpen = database->open( IO_ReadOnly );

I am not sure how to tackle this. In Unix its so easy to assume that readers won't stop writers from writing. I think cygwin needs different semantics, wherein its the kded which does the update to the database always (currently eny process can call kbuildsycoca to update the database) and everybody, who has the database opened, is asked to close the database before rename() is issued by kded?
Comment 1 devsk 2005-05-10 17:36:12 UTC
following comes from Ralf on kde-cygwin list, and its more detailed:

--------------------------------------------------------------------------------
all access to the ksycoca database is encapsulated  by the class 
KSycoca, 
which contains a signal handler notifyDatabaseChanged(), wich closes 
the 
database. from kdecore/ksycoca.cpp

void KSycoca::notifyDatabaseChanged(const QStringList &changeList)
{
    d->changeList = changeList;
    //kdDebug(7011) << "got a notifyDatabaseChanged signal !" << endl;
    // kded tells us the database file changed
    // Close the database and forget all about what we knew
    // The next call to any public method will recreate
    // everything that's needed.
    closeDatabase();

    // Now notify applications
    emit databaseChanged();
}

The relating signal is send after updating the database in 
kbuildsycoca:kdemain():

   if (args->isSet("signal"))
   {
     // Notify ALL applications that have a ksycoca object, using a 
broadcast
     QByteArray data;
     QDataStream stream(data, IO_WriteOnly);
     stream << *g_changeList;
     dcopClient->send( "*", "ksycoca", 
"notifyDatabaseChanged(QStringList)", 
data );
   }

The problem is that this signal is send to late. A workaround could be 
to send 
this signal immediatly before the call to KBuildSycoca::recreate() in 
kbuildsycoca:kdemain()

      KBuildSycoca *sycoca= new KBuildSycoca; // Build data base
      if (args->isSet("track"))
         
sycoca->setTrackId(QString::fromLocal8Bit(args->getOption("track")));
>>>>>>>>>>>>>>>>>>>> 
   if (args->isSet("signal"))
   {
     // Notify ALL applications that have a ksycoca object, using a 
broadcast
     QByteArray data;
     QDataStream stream(data, IO_WriteOnly);
     stream << *g_changeList;
     dcopClient->send( "*", "ksycoca", 
"notifyDatabaseChanged(QStringList)", 
data );
   }
<<<<<<<<<<<<<<<<<<<
      if (!sycoca->recreate()) {

but I don't do if this works in any cases. 

A generic approach is to introduce a new signal for example 
notifyDatabaseUpdatePending or so, which could close and lock any 
unwanted 
access to the database until the notifyDatabaseChanged is send. If the 
workaround doesn't work we can implemented this.
-------------------------------------------------------------------------------
Comment 2 Waldo Bastian 2005-05-10 18:10:44 UTC
notifyDatabaseUpdatePending would need to call all clients one-by-one and wait for each call to return. Even then it's still not guaranteed to work. Whatever solution you come up with, please do not change the behavior under unix.
Comment 3 devsk 2005-05-11 07:05:55 UTC
what is the right solution in your mind then?
Comment 4 Ralf Habacker 2005-05-11 19:11:44 UTC
> notifyDatabaseUpdatePending would need to call all clients one-by-one and wait for each call to return. 

This could be done by using the synchronous call() method instead of the asynchronous send(). Unfortunally call() could not send the messages to all related applications instead The applications has to be enumerated. Currently I don't know which dcop function could be used. Any hints ?  

>Even then it's still not guaranteed to work.
Why 
Comment 5 Waldo Bastian 2005-05-12 00:02:19 UTC
You can use DCOPClient::registeredApplications() to get a list of all apps.

> >Even then it's still not guaranteed to work. 
> Why 

A new application can start in the mean time and open the database.
Comment 6 Ralf Habacker 2005-05-12 17:45:52 UTC
> A new application can start in the mean time and open the database. 
I don't thoughed about
 
What about if kbuildsycoca would update the database, rename it to a new (unused) name, store the name in a configfile and send all related applications the new name for updating their database ? 

I saw that the name is currently hardcoded in from kdecore/ksycoca.cpp 
-- path = KGlobal::dirs()->saveLocation("cache") + "ksycoca";
Comment 7 Ralf Habacker 2005-05-12 21:31:12 UTC
For the record: One problem is that kio_xxx processes does not have a dcop instance and does not handle dcop signals, so they don't got any notice about the ksycoca database change :-(. 


 
Comment 8 Ralf Habacker 2005-05-14 17:19:10 UTC
> they don't got any notice about the ksycoca database change

One question: is this a feature or a whole in the design ? 
Comment 9 devsk 2005-05-15 01:31:29 UTC
this is what I did temporarily. Basically, close the database in all before recreate and execute normally. hopefully recreate() takes long enough before issuing rename() that 
existing apps can close() and but short enough that no other application comes along to open the database. This doesn't solve the problem in all corner cases.

-------------------------------------------------------------
$ diff -u kdecore/ksycoca.kidl.orig kdecore/ksycoca.kidl     
--- kdecore/ksycoca.kidl.orig   2005-05-14 15:48:22.000000000 -0700
+++ kdecore/ksycoca.kidl        2005-05-14 15:59:08.000000000 -0700
@@ -12,6 +12,10 @@
     <SUPER>DCOPObject</SUPER>
     <FUNC>
         <TYPE>void</TYPE>
+        <NAME>closeDatabase</NAME>
+     </FUNC>
+    <FUNC>
+        <TYPE>void</TYPE>
         <NAME>notifyDatabaseChanged</NAME>
         <ARG><TYPE  qleft="const" qright="&amp;">QStringList</TYPE></ARG>
      </FUNC>

$ diff -u  kded/kbuildsycoca.cpp.orig kded/kbuildsycoca.cpp     
--- kded/kbuildsycoca.cpp.orig  2005-05-14 15:34:28.000000000 -0700
+++ kded/kbuildsycoca.cpp       2005-05-14 16:02:20.000000000 -0700
@@ -908,6 +908,13 @@
       KBuildSycoca *sycoca= new KBuildSycoca; // Build data base
       if (args->isSet("track"))
          sycoca->setTrackId(QString::fromLocal8Bit(args->getOption("track")));
+
+   if (args->isSet("signal"))
+   {
+     // Notify ALL applications that have a ksycoca object, using a broadcast
+     dcopClient->send( "*", "ksycoca", "closeDatabase()", "");
+   }
+
       if (!sycoca->recreate()) {
 #ifdef KBUILDSYCOCA_GUI
         if (!silent || showprogress)

------------------------------------
Comment 10 lexual 2010-01-23 06:06:43 UTC
Still relevant to recent KDE version?
If not please close the bug.

Thanks,

Lex.
Comment 11 David Faure 2015-09-19 23:04:16 UTC
ksycoca uses shared memory on Windows these days, this problem is long gone.