64位Windows 7删除包含Sqlite的目录时,删除递归目录失败

时间:2015-02-12 10:24:27

标签: sqlite fileapi

我有一个测试用例(更大的应用程序的剥离版本)在尝试删除目录或文件时偶尔会在Windows 7计算机上失败。它使用本机Windows API。 测试执行以下步骤 -

  1. 创建目录。
  2. 在步骤#1创建的目录中创建一个sqlite3数据库。
  3. 在数据库中创建一个表。
  4. 关闭数据库。
  5. 如果存在任何DB日志文件,请将其删除。
  6. 删除数据库文件。
  7. 删除目录。
  8. 如果您跳过步骤#1,那么测试运行正常。 如果在步骤#4和步骤#5之间添加延迟,则测试运行正常。

    #include <windows.h>
    #include <string>
    #include <iostream>
    #include <stdio.h>
    #include <direct.h>
    #include <sqlite3.h>
    #include <io.h>
    using namespace std;
    
    bool sleep_for_sometime = false;
    bool create_table = false;
    bool runTest()
    {
        char cwdpath[1024] = {'\0'};
        if (_getcwd(cwdpath, 1023) == NULL) return false;
        string testpath(cwdpath);
        testpath += "/test_dir";
        _mkdir(testpath.c_str());
        string dbpath = testpath + "/test.db";
        sqlite3 *db = NULL;
        if (sqlite3_open(dbpath.c_str(), &db) != SQLITE_OK) return false;
        // Create table.
        if (create_table) {
            string sql = "CREATE TABLE COMPANY(" \
                      "ID INT PRIMARY KEY  NOT NULL," \
                      "NAME           TEXT NOT NULL," \
                      "AGE            INT  NOT NULL)";
            char *zErrMsg = 0;
            if (sqlite3_exec(db, sql.c_str(), NULL, 0, &zErrMsg) != SQLITE_OK) {
                sqlite3_close(db);
                cerr << "Could not create table: " << zErrMsg << endl;
                return false;
            }
        }
        sqlite3_close(db);
        if (sleep_for_sometime) {
            Sleep(100);
        }
        string journal_file = dbpath + "-journal";
        string journal_error = journal_file + " failed";
        if (_access(journal_file.c_str(), 06) == 0) {
            if (_unlink(journal_file.c_str()) != 0) {
                perror(journal_error.c_str());
                return false;
            }
            cout << "journal file --" << journal_file << endl;
        }
        string db_error = dbpath + " failed";
        if (_unlink(dbpath.c_str()) != 0) {
           perror(db_error.c_str());
           return false;
        }
        string dir_error = testpath + " failed";
        if (_rmdir(testpath.c_str()) != 0) {
           perror(dir_error.c_str());
           return false;
        }
        return true;
    }
    int main(int argc, char **argv)
    {
        cout << "Usage: ./projtest 1 1" << endl;
        cout << "------If you pass two parameter, then always create table and sleep for some time." << endl;
        cout << "Usage: ./projtest 1" << endl;
        cout << "------If you pass one parameter, then always create table, but don't sleep." << endl;
        cout << "Usage: ./projtest" << endl;
        cout << "------If you don't pass any parameter, then don't create table and don't sleep." << endl;
        if (argc == 3) {
          sleep_for_sometime = true;
          create_table = true;
        } else if (argc == 2) {
          create_table = true;
          sleep_for_sometime = false;
        } else {
          create_table = false;
          sleep_for_sometime = false;
        }
        for (int i = 0; i < 500 ; i++) {
            if (! runTest()) {
                cerr << "Err in runTest trial --" << i+1 << endl;
                return 1;
            }
        }
        return 0;
    }
    

1 个答案:

答案 0 :(得分:0)

可能是活动文件锁sqllite强制它在步骤4和5之间没有及时清除。 请记住,与硬盘相比,内存中的代码以闪存速度运行。 对硬盘的sql lite进程指令很可能仍然忙于将数据写入文件,在日志中输入条目,然后释放文件处理程序,而代码已经忙于删除它。

因此,虽然sql lite完成并且使用了,但是硬盘仍在勾选其写入数据和清除锁定的工作列表。

也许你可以实现一个while循环来检查文件是否被锁定,然后耐心地等待释放锁,或者标记文件以备将来删除并每50ms重新检查一次。

修改 补充澄清

事情是这里有两个问题。硬盘速度和CPU速度。在正常的程序代码中,当您将写入指令推送到硬盘时,它将被放入驱动程序/驱动器本身的队列中以进行写入,并且驱动程序/硬盘一个接一个地工作(ish)。

程序将收到一条消息,表明操作已经被调整,因此可以继续执行。为什么程序在继续正确之前必须等待一两毫秒?由于硬盘驱动器很慢并且有自己的优先级来阅读和写东西。它会做到这一点,但现在不完全导致它现在正在读取文件或其他东西。

因此,虽然文件可以自由编辑的写入操作仍然在要写入的队列中,但它已经发送了删除请求,但该文件尚未被标记为可写,因为该操作仍在队列中。而错误来自。