Archived

This forum has been archived. Please start a new discussion on GitHub.

How to solve this ...

If server has Object A and B, and client has Proxys of Object A and B. When the client want to call a method of A through RPC, how the client indicates Object B to the server.
For example:
In a mmorpg, Object A is a character controled by player, Object B is a monster. And in the client the player control the Proxy A to ask Object A to attack B (the monster). I want this to be done by call a method of A, and use the parameters to indicate the target of the attack. The performance here is important because this kind of call will be used very often and almost everywhere.
Another question is I want to use the Command pattern based on RPC like above. And that means I will pass (Message) Objects to implement the interaction of objects. I will store the Message Classes in dll file so they can be load dynamicly during the running of the server. So is there any feature in ICE to achieve these easily? And is there something bad of these I should know?

Thank you.
«1

Comments

  • Hi sjinny,

    You can pass proxy references between ICE servants. In your slice definitions, you use pointer syntax to indicate a proxy :-
    module Monster {
      interface ObjectB { };
      interface ObjectA {
        void func(ObjectB* target);
      };
    };
    

    In C++, ObjectA receives a proxy reference :-
    void func(const ::Monster::ObjectBPrx&);
    

    Regarding performance, be aware that if ObjectA doesn't reside on the same host+adapter as ObjectB, then there is a cost in establishing the connection from ObjectA's host+adapter to ObjectB's host+adapter. Also be aware of distributed deadlock potential with nested synchronous calls between ObjectA/ObjectB, etc which are a real concer with increased server-to-server invocations.

    You could support a basic Command implementation with ICE classes, which are local, polymorphic and support both data members and operations (pretty neat for a distributed platform!).
    module Demo {
      interface Monster { };
      class Command {
        void execute();
      };
      class KillMonsterCmd extends Command {
        Monster* target;
      };
      interface ObjectA {
        void doCmd(Command cmd);
      };
    };
    

    You need to be aware that ICE classes are local, not remote, so when you create a KillMonsterCmd on the client side and pass it to ObjectA, you are passing the state (data members) whereas the operations (code) are created via factory. It's possible for the client to send an unsupported class (slice definitions get out of sync) in which case the class will be sliced to its most-derived super-class. You might write the generic Command.execute() to throw an UnknownCommand exception to indicate this slicing..

    HTH
  • Tanks a lot joe~
    And...
    As you said, it seems that server will receive a Proxy of the target. So if the Command wants to handle the data members of the target directly, could the server get the actual object from the Proxy ?
    Regarding performance, be aware that if ObjectA doesn't reside on the same host+adapter as ObjectB, then there is a cost in establishing the connection from ObjectA's host+adapter to ObjectB's host+adapter.
    I heard that many MMO games uses several servers to simulate a single game world, and the servers are certainly in a lan network. In this situation, will the establishing of the connection between the servers cost a lot ? And the connection between two server will need to be established only once right?

    And are there many Command pattern using in the MMO games ? Does it perform good ?

    Thank you~
  • benoit
    benoit Rennes, France
    sjinny wrote:
    Tanks a lot joe~
    And...
    As you said, it seems that server will receive a Proxy of the target. So if the Command wants to handle the data members of the target directly, could the server get the actual object from the Proxy ?

    No, there's no way to do this directly. You'll have to implement a method on the target that you can call to retrieve some of its attributes. This method could also transfer the object over the wire but that's probably not a good idea if this object has many attributes.
    I heard that many MMO games uses several servers to simulate a single game world, and the servers are certainly in a lan network. In this situation, will the establishing of the connection between the servers cost a lot ? And the connection between two server will need to be established only once right?

    And are there many Command pattern using in the MMO games ? Does it perform good ?

    Thank you~

    The connection between the 2 servers will be estalished once and it's evenutally reused for many requests. If there's no requests for a while on the connection, it might be closed (depending on the active connection mangement settings, aka ACM, see the Ice manual for more information). I don't think should worry about the cost of connection establishement. However, you should carrefully design your interfaces to not make too many RPC calls: even though Ice is fast, an RPC call is still more expensive than a call on a local object! As joe mentioned, you should also be aware of deadlocks... I recommend you to read the two "Avoiding Deadlocks" articles from our newsletter on this subject.

    Using the command pattern seems like a good idea as long as you're aware that RPC calls are more expensive and more deadlock prone than regular calls on objects :)

    Cheers,
    Benoit.
  • Thanks, benoit~
    And I'm wondering which design patterns I should know for the network programing and ICE using.

    Thank you~
  • Another problem:
    module Demo {
      interface Monster { };
      class Command {
        void execute();
      };
      class KillMonsterCmd extends Command {
        Monster* target;
      };
      interface ObjectA {
        void doCmd(Command cmd);
      };
    };
    
    In this example, the class derived from Command is also in the same .ice definition. This would become a problem to add NEW class derived from Command when the server is still running. Would it be possible to add new classes into a existing and running system and let ICE run-time could send & receive the instance of the new class ?

    thanks~
  • benoit
    benoit Rennes, France
    Adding a new class and its implementation at runtime should be possible. However, there's no built-in mechanism in Ice to do this -- you'll have to do something like the following:
    • You'll need to dynamically load the code for the implementation of the new class. How to do this depends on the programing language environment. For example, in C++, this could be done by dynamically loading a shared library, in Java, you would load a new class.
    • Then, you'll need to register a new object factory with the communicator for the new type.

    Cheers,
    Benoit.
  • How to get a proxy pointing to a existing object(which implements the ICE interface) from the existing object ?

    Here is my .ice file:
    module MMO
    {
    
    interface ObjectIce
    {
    };
    
    interface SceneIce;
    class MessageIce
    {
    	void exec( SceneIce* scene );
    	void setFrom( ObjectIce* from );
    };
    
    interface SceneIce
    {
    	void sendMsg( MessageIce msg );
    };
    
    };
    

    I let the client send the Message to the server's scene and when the scene process the messages the scene will call msg.exec(). In the ice definition the exec() has a parameter that will tell the Message in which scene it is. But after compiling the parameter type is const ::MMO::SceneIcePrx& but in the scene's breathe() function I only got the this pointer whose type is Scene*. So how to get a proxy pointing to the scene for passing as the parameter of the exec() ?

    Thank you~
  • benoit
    benoit Rennes, France
    You can create a proxy for the current Ice object in the implementation of a dispatched method with the following code:
    void 
    SceneIceI::sendMsg(const MessageIcePtr& msg, const Ice::Current& current)
    {
         SceneIcePrx self = SceneIcePrx::uncheckedCast(current.adapter->createProxy(current.id));
         msg->exec(self);
    }
    

    You could also cache the proxy as an attribute of the servant implementation.

    Cheers,
    Benoit.
  • benoit wrote:
    You can create a proxy for the current Ice object in the implementation of a dispatched method with the following code:
    void 
    SceneIceI::sendMsg(const MessageIcePtr& msg, const Ice::Current& current)
    {
         SceneIcePrx self = SceneIcePrx::uncheckedCast(current.adapter->createProxy(current.id));
         msg->exec(self);
    }
    

    You could also cache the proxy as an attribute of the servant implementation.

    Cheers,
    Benoit.

    The exec() is :
    virtual void exec( const ::MMO::SceneIcePrx& __scene, const ::Ice::Current& __current= ::Ice::Current() );
    The parameter __scene is a reference (which is generated by the slice compiler).
    In your code here the self is in the stack and seems to be deleted after the sendMsg calling. Will this course any memory problem ?
    :confused:
  • marc
    marc Florida
    No, this won't cause a memory problem, because self is deleted after the call to exec() returns.
  • I've wrote some codes and I wrote a class World with a breathe() method. I wrote the methods like this when I use my own connection system:
    void World::breathe()
    {
        _msg_router->route();
        _scene->breathe();
        _connections->breathe();
    }
    void World::run()
    {
        if( _msg_router && _scene && _connections ){
            while( _running ){
                switch( _state ){
                    case RUNNING:
                        breathe();
                        break;
                    case PAUSE:
                        break;
                    case SHUT_DOWN:
                        _running= false;
                        break;
                    default:
                        break;
                }
            }
        }
    }
    
    But now I see when using ICE it seems that the code "ic->waitForShutdown();" would course the process blocked so I can't simply write the breathe() method like this:
    void World::breathe()
    {
        _msg_router->route();
        _scene->breathe();
        _connections->breathe();
        ic->waitForShutdown();
    }
    
    So what should I do ? Create a separate thread for the "ic->waitForShutdown();"? The RPC from clients would manipulate the scenes and the scenes have their own breathe() so I would have to add lock for the scenes and that would reduce the performance.
    :confused:
  • benoit
    benoit Rennes, France
    Hi,

    Sorry but I don't understand what you're trying to do. Why are you calling waitForShutdown() here and what do you expect from this call?

    Cheers,
    Benoit.
  • Sorry I think I should read the manual more carefully, but anyway how should I do to achieve this:
    I have a main loop and in this loop wo want to call my scene::breathe() so that it can handle it's own affairs and I also need to make ICE running and I prefer one single thread rather than multi-threading to avoid the mutex troubles.

    Thanks~
  • benoit
    benoit Rennes, France
    Hi,

    Ice is inherently multi-threaded so incoming requests will always be dispatched in seperate threads (thread from the server thread pool if you use the thread pool concurrency model for example). So you don't need to call anything in your main loop, however, I'm afraid you'll have to deal with synchronization.

    Cheers,
    Benoit.
  • benoit wrote:
    Adding a new class and its implementation at runtime should be possible. However, there's no built-in mechanism in Ice to do this -- you'll have to do something like the following:
    • You'll need to dynamically load the code for the implementation of the new class. How to do this depends on the programing language environment. For example, in C++, this could be done by dynamically loading a shared library, in Java, you would load a new class.
    • Then, you'll need to register a new object factory with the communicator for the new type.

    Cheers,
    Benoit.

    Do I MUST write the definitions of derived classes from Message into the Slice file?
  • benoit
    benoit Rennes, France
    Hi,

    Yes, you have to write derived class definitions in Slice if want to transmit instances of these derived classes over the wire with Ice.

    Cheers,
    Benoit.
  • T.T
    So much work to do...

    Thanks~
  • Q1:
    Now I know that something should be locked before operations, but how do I know which datas or operations should have mutexes ?

    Q2:
    How does TryLock Object behave when failing to lock ?

    Thank you~
  • matthew
    matthew NL, Canada
    Note that these are not Ice specific questions, and therefore they are out of scope of the support we provide on this forum. You should really get a decent MT programming book such as "Programming with POSIX threads" by David Butenhof, or "Concurrent Programming in Java: Design Principles and Patterns" by Doug Lea.
    Q1:
    Now I know that something should be locked before operations, but how do I know which datas or operations should have mutexes ?

    Simply put you must protect all data that can be concurrently accessed. If you have multiple server side threads then you must inspect your implementation to determine this. The simplest strategy is to have a single mutex that protects all data, however, this is the most coarse grained approach in that all operations will be serialized. We have written articles on this topic in the newsletter. I suggest reading them for some ideas.
    Q2:
    How does TryLock Object behave when failing to lock ?

    If the mutex is already locked you will get a ThreadLockedException.
  • I know a little about the multi-thread programing. In Q1 I meant because I don't know which datas would be touched by the threads of ICE, so I ask which data should be protected.;)
  • matthew
    matthew NL, Canada
    sjinny wrote:
    I know a little about the multi-thread programing. In Q1 I meant because I don't know which datas would be touched by the threads of ICE, so I ask which data should be protected.;)

    I'm not sure what you mean. Ice will not touch your servant state by itself (unless you are talking about using the Freeze evictor in which case you must protect the class state in a different way. See the Ice manual for more details on this.)

    I also made a mistake with my description of the TryLock semantics above. You should call acquired on the TryLock to find out whether the current thread has acquired the lock. You will get a ThreadLockedException if the current thread already has the lock and the mutex is non-recursive (and you are not using POSIX threads in release mode).
  • So I ask in this way :
    Which datas will be manipulate by the threads of ICE and the thread of the main() function ? :confused:
  • benoit
    benoit Rennes, France
    Objects from the Ice runtime (the communicator, the object adapter, etc) are accessed by Ice threads and the main thread. However, you don't have to worry about synchronization here: the implementation of these objects is already thread-safe.

    You just need to worry about synchronization for the implementation of your servant operations if they modify some state. For an example, see the demo/Ice/session demo from your Ice distribution. The implementation of the Session servant (SessionI) uses synchronization to protect access to the _destroy flag.

    Cheers,
    Benoit.
  • That's the answer I want~ Thank you~
    And now it seems that I can avoid many troubles from the mutexes by using RPC to pass Command Object to the Server and then let a single thread( the main thread ) to process them and anything else.:D
    Can I say less RPC means more performence ?
  • matthew
    matthew NL, Canada
    RPC's by their very nature are definitely slower than local invocations, yes.

    If you use the default settings for a server it will only have one thread active in a servant at any time, and thereby you don't need any synchronization.
  • I have difficulties to implement a multi-thread safe queue. :( Is it possible to use the main() thread to process the PRC so that the servant's method will running in the main() thread?
  • benoit
    benoit Rennes, France
    Hi,

    No, there's no easy way to do this. If you didn't already, you could have a look at demo/IceUtil/workqueue for a sample implementation of a work queue.

    Cheers,
    Benoit.
  • I see the demo is simply locked for the whole queue but I think when the number of a list >= 2 it should be ok to have one thread pushing while another thread is poping.
    Thanks anyway~ At least this is a way if I can't implement a multi-thread safe queue.
  • benoit
    benoit Rennes, France
    Sorry, I'm not sure I understand your comment. The lock is only held to add and get work items. How is it different from the queue you're trying to implement?

    Cheers,
    Benoit.
  • I think in the demo at one time you can only get an item or put an item, you can't get the item in the front of the queue and put an item into the end of the queue at the same time.:o