Hi all,

I am not sure how to explain exactly what I am hoping to do here, but I will do my best in the hopes that someone will understand.

I am writing a program that uses a cache, this cache is used with a thread that is seperate from the main program, this works perfectly, the cache is a seperate class.

Now the problem, the cache gets updated hourly from an external file, basically the method is that every minute or so the program checks the stat on the file and if the atime has altered it rebuilds the cache, now I need my thread that is using the cache to use totally transparently the new cache.

Since the building of the new cache every hour is handled by a seperate thread I can't figure out a sane method of updating the class in use by the first thread.

I have tried writing a seperate class called cache_control that works basically to build references ie:

class cache_control {
  public:
     Cache private_cache;
     Cache public_cache;
     void reinit_cache(void);
}

void cache_control::reinit_cache(void) {
   private_cache.read_input_file();
   Cache & pubic_cache = private_cache;
}

Please excuse the roughness of this code it is just an example of the type of thing I am attempting to do.

As you see I am making 2 caches the private and the public the public_cache is just a reference to the private_cache object and when my thread is started it is passed a reference to the main cache_control object from main() and it uses the public_cache it access its methods and data, that way when I need to rebuild the cache I just reinit_cache the private_cache and it then points public_cache to the new instance of private_cache.

I hope you are with me so far.

The problem I have here is that firstly I am creating the object Cache public_cache before I get the chance to declare it as a reference and I don't think this is allowed (I may be wrong) and secondly, I think that perhaps having a reference to a class that references a class that isn't inherited, is perhaps a dangerous thing to do, and thirdly it just plain doesn't seem to work.

I am basically looking for a good suggestion from someone on a better way of doing this, I though perhaps I should make my Cache class in inherited class from cache_control but the way I see it is that will just make it a little cleaner it won't actually make it work, I also considered creating a global vector something like:

vector<cache> global_cache;

Then when I need to rebuild the cache I simply build a new instance of the cache delete the instance currently in the vector and add the new one, but I think perhaps that will create some more problems in itself not to mention the fact that my test programs using a vector with type <cache> all seem to die at run time no matter that I do with them.

What I am hoping to avoid is having to kill the thread and restart it with a new copy of the data is the program itself is a fairly high demand searchengine and by killing the thread I would have to kill all client connections.

Any suggestions would be very welcome I have been pulling my hair out on this one for a couple of days, if anymore explaination of what I am hoping to do is needed please let me know.

Thanks

Ben

Recommended Answers

All 14 Replies

So you have a cache maintained by the main process thread and one or more other threads that access the cache (you said one, but heck). And your question, if I understand it, is:

"How can I prevent the non-main threads from accessing the cache while it is being rebuilt by the main thread?"

So, if that is the basic question then.....

The way you do that is with a 'critical section'. A 'critical section' can only be in use by one thread at a time. If another thread wants access to the critical section, it is blocked until the critical section becomes free.

All real os' have this, here's how it might look in Windows:

CRITICAL_SECTION threadLock;
::InitializeCriticalSection( &threadLock ); // initialize this ONCE
...

int cache::ReadCache()
{
    int rtn;

        // to get access to the cache, call this:
    ::EnterCriticalSection( &threadLock );

        // now you are the ONLY thread in this code, others will wait for you...
    rtn = vartogetfromthecache;

        // you must leave the critical section, or things stay locked.
    ::LeaveCriticalSection( &threadLock );

    return rtn;
}

Since threads will wait, you want the critical section to NOT TAKE LONG, or it will gum everything up. Instead, use it as a wrapper around accesses to the data. When updating from the database, you will want to get the data all ready to go (reading the disk OUTSIDE the critical section since thats slow), and then enter the critical section, copy in your new data, end exit the critical section.

Although I realized after posting that, what you WANTED to know was how to make a reference to a class....

AClass classInstance;
AClass *pClassInstance; // pointer to the class
AClass &rClassInstance; // reference to the class

references are really pointers internally, but with the syntactic nicety that you don't use ->, but just use .

classInstance.SetVal(12);
pClassInstance->SetVal(12);
rClassInstance.SetVal(12);

Like a pointer, a reference must refer to something:

pClassInstance = &classInstance; // pClassInstance now POINTS TO classInstance
rClassInstance = classInstance; // rClassInstance now REFERS to classInstance

A reference has no space allocated for it's object, the same as a pointer.

commented: Excellent help, really good knowledge! +1

Although I realized after posting that, what you WANTED to know was how to make a reference to a class....

AClass classInstance;
AClass *pClassInstance; // pointer to the class
AClass &rClassInstance; // reference to the class

references are really pointers internally, but with the syntactic nicety that you don't use ->, but just use .

classInstance.SetVal(12);
pClassInstance->SetVal(12);
rClassInstance.SetVal(12);

Like a pointer, a reference must refer to something:

pClassInstance = &classInstance; // pClassInstance now POINTS TO classInstance
rClassInstance = classInstance; // rClassInstance now REFERS to classInstance

A reference has no space allocated for it's object, the same as a pointer.

Okay that is looking a lot like what I was looking for, so is it possible to define the pointer (reference) to the class and then actually decide where it point it to?

ie:

class SomeClass {
   public:
      Aclassname actualclass;
      Aclassname *pointerclass;
   public:
      void makepointer(void) {
         pointerclass = &actualclass;
      }
}

On looking at your code that looks like it is okay, which is exactly what I wanted, for the other half though, if later on I wanted to point the pointer to a different class without deleting the pointer how would I do that?

ie

void point_else_where(void) {
   delete pointerclass;
   pointerclass = &newactualclass;
}

As you can see I know exactly what I am wanting to do it is really all a matter of syntax, I haven't been programming C++ for long enough to have encountered a lot of this stuff.

Thanks for the help on the first part if you can give me a prod in the right direction on the second part I would be eternally grateful :D

Thanks again

Ben

Since you said "pointerclass = &actualclass;", you don't want to try to DELETE the pointer pointerclass, because it doesn't point to allocated space!

So, two ways suggest themselves:

// **METHOD ONE**
AClassname c[2];
AClassname* pCurrentOneInUse;
int whichClassIsInUse;
...
whichClassIsInUse = 0;
pCurrentOneInUse = c[whichClassIsInUse];
...
void swapclasses()
{
    // here, we assume you've filled in the NON-current AClassname with the
    // new data; that's a bit clunky because you have to do a
    // whichClassIsInUse test like I do here to pick the class NOT in use.
    // If I were a hacker, I might just say (!whichClassIsInUse), but
    // that isn't as clear.  :-)
    if (whichClassIsInUse == 0)
        whichClassIsInUse =  1;
    else
        whichClassIsInUse = 0;
    pCurrentOneInUse = c[whichClassIsInUse];
}

Method one has the advantage that there is no memory allocations, everything is static. That might be a tad faster, all other things being equal. It just seems kinda clunky to me, though.

//***METHOD TWO***
AClassname* pCurrentOneInUse;
...
pCurrentOneInUse = NULL;
...
void swapclasses( AClassname* withThis )
{
    // here, we assume you have a local var that you filled in the new data
    // with and passed into this routine
    delete pCurrentOneInUse;
    pCurrentOneInUse = new AClassname( *withThis ); // use copy constructor
    // or, if no copy constructor, copy the contents HERE
    // or, the caller could do the new and here you just point to the space
}

Chainsaw, now that looks perfect :) I am gonna start playing around with those methods now :)

As promised you have my eternal gratitude!!

Thanks again

Ben

Okay I am having a bit of a problem now, this is basically the test class I have written:

Header:

class g_cache{
public:
    keyword_cache* Kcache;
    keyword_cache referenced_kcache;
    SKU_cache Scache;
    void Set_Kcache_Reference(void);
public:    
    g_cache();
    ~g_cache();
};

Main:

g_cache::g_cache()
{
   g_cache::Set_Kcache_Reference();     
}


g_cache::~g_cache()
{
}

void g_cache::Set_Kcache_Reference(void)
{
   referenced_kcache.read_output_file();
   keyword_cache Kcache = referenced_kcache;
   string word = "black";
   
   cout << "Test1" << endl; 
   referenced_kcache.find_keyword(word);   

   cout << "Test2" << endl; 
   Kcache.find_keyword(word);      
}

This bit works when I initalise the class the output is correct both times.

However:

g_cache caches; 

  string word = "black";
  caches.Kcache.find_keyword(word);

Doesn't even compile it gives the following error:

vsearch.cpp: In function `int main(int, char**)':
vsearch.cpp:293: error: request for member `find' in `caches.g_cache::Kcache',
which is of non-aggregate type `keyword_cache*'

Any suggestions.

Thanks

Ben

Kcache is a pointer, so you need -> rather than .

caches.Kcache->find_keyword(word);

Kcache is a pointer, so you need -> rather than .

caches.Kcache->find_keyword(word);

That will allow it to compile but it causes a segfault when it does compile, which is interesting since if I use the actual class that is pointed to it runs fine, it is only when I use the pointer class that it will segfault.

Ben

Do you ever set Kcache = &referenced_cache?

A segment fault means the pointer is pointing into random memory.

Hmmm when I tried doing that I got the following error:

Kcache = &referenced_kcache;

cache.cpp: In member function `void g_cache::Set_Kcache_Reference()':
cache.cpp:26: error: conversion from `keyword_cache*' to non-scalar type 'keyword_cache' requested

or:

g_cache::Kcache = &referenced_kcache;

Gives:
cache.cpp: In member function `void g_cache::Set_Kcache_Reference()':
cache.cpp:33: error: request for member `find_keyword' in `
this->g_cache::Kcache', which is of non-aggregate type `keyword_cache*'

Or:

keyword_cache Kcache = &referenced_kcache;

Gives:

cache.cpp: In member function `void g_cache::Set_Kcache_Reference()':
cache.cpp:26: error: conversion from `keyword_cache*' to non-scalar type `keyword_cache' requested

Ya, hard to tell from out here in webland, but it sounds like Kcache is not declared as a pointer (as in your last code snippet, where it isn't).

keyword_cache* Kcache = &referenced_kcache; // should work

Chainsaw,

You are a genius man, seriously now it is working perfectly, I am very grateful, I would give you some more reputation but I already did before so it won't let me, but thanks a lot for your help.

How long have you been programming?

Thanks again very appreciative

Ben

Since 1978 :-(

Only 20 years to go and I can retire. :-(

Wow the same year I was born!

Thanks for the help with this though I was in a real bind my boss wants this program ready for testing in 3 weeks and this was my last major obstacle (I hope) :)

Thanks again for the help man!

Ben

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.