(file) Return to AsyncOpNode.h CVS log (file) (dir) Up to [Pegasus] / pegasus / src / Pegasus / Common

  1 karl  1.44 //%2005////////////////////////////////////////////////////////////////////////
  2 mike  1.2  //
  3 karl  1.42 // Copyright (c) 2000, 2001, 2002 BMC Software; Hewlett-Packard Development
  4            // Company, L.P.; IBM Corp.; The Open Group; Tivoli Systems.
  5            // Copyright (c) 2003 BMC Software; Hewlett-Packard Development Company, L.P.;
  6 karl  1.39 // IBM Corp.; EMC Corporation, The Open Group.
  7 karl  1.42 // Copyright (c) 2004 BMC Software; Hewlett-Packard Development Company, L.P.;
  8            // IBM Corp.; EMC Corporation; VERITAS Software Corporation; The Open Group.
  9 karl  1.44 // Copyright (c) 2005 Hewlett-Packard Development Company, L.P.; IBM Corp.;
 10            // EMC Corporation; VERITAS Software Corporation; The Open Group.
 11 mike  1.2  //
 12 mike  1.5  // Permission is hereby granted, free of charge, to any person obtaining a copy
 13 chip  1.30 // of this software and associated documentation files (the "Software"), to
 14            // deal in the Software without restriction, including without limitation the
 15            // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 16 mike  1.2  // sell copies of the Software, and to permit persons to whom the Software is
 17            // furnished to do so, subject to the following conditions:
 18 david.dillard 1.45 //
 19 chip          1.30 // THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN
 20 mike          1.2  // ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED
 21                    // "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
 22 chip          1.30 // LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 23                    // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 24                    // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 25 mike          1.2  // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 26                    // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 27                    //
 28                    //==============================================================================
 29                    //
 30                    // Author: Mike Day (mdday@us.ibm.com)
 31                    //
 32 a.arora       1.40 // Modified By: Amit K Arora, IBM (amita@in.ibm.com) for Bug#1188
 33 a.arora       1.41 //              Alagaraja Ramasubramanian (alags_raj@in.ibm.com) for Bug#1090
 34 david.dillard 1.45 //              David Dillard, VERITAS Software Corp.
 35                    //                  (david.dillard@veritas.com)
 36 mike          1.2  //
 37                    //%/////////////////////////////////////////////////////////////////////////////
 38                    
 39                    
 40                    #ifndef Pegasus_AsyncOpNode_h
 41                    #define Pegasus_AsyncOpNode_h
 42                    
 43                    #include <Pegasus/Common/Config.h>
 44                    #include <Pegasus/Common/Message.h>
 45                    #include <Pegasus/Common/OperationContext.h>
 46                    #include <Pegasus/Common/internal_dq.h>
 47                    #include <Pegasus/Common/IPC.h>
 48 kumpf         1.35 #include <Pegasus/Common/Linkage.h>
 49 mike          1.2  
 50                    PEGASUS_NAMESPACE_BEGIN
 51 mike          1.5  
 52 mike          1.2  #define ASYNC_OPFLAGS_UNKNOWN           0x00000000
 53                    #define ASYNC_OPFLAGS_INTERVAL_REPEAT   0x00000010
 54                    #define ASYNC_OPFLAGS_INDICATION        0x00000020
 55                    #define ASYNC_OPFLAGS_REMOTE            0x00000040
 56                    #define ASYNC_OPFLAGS_LOCAL_OUT_OF_PROC 0x00000080
 57                    #define ASYNC_OPFLAGS_PHASED            0x00000001
 58                    #define ASYNC_OPFLAGS_PARTIAL           0x00000002
 59                    #define ASYNC_OPFLAGS_NORMAL            0x00000000
 60                    #define ASYNC_OPFLAGS_SINGLE            0x00000008
 61                    #define ASYNC_OPFLAGS_MULTIPLE          0x00000010
 62                    #define ASYNC_OPFLAGS_TOTAL             0x00000020
 63 mday          1.13 #define ASYNC_OPFLAGS_META_DISPATCHER   0x00000040
 64 mday          1.17 #define ASYNC_OPFLAGS_FIRE_AND_FORGET   0x00000080
 65                    #define ASYNC_OPFLAGS_SIMPLE_STATUS     0x00000100
 66                    #define ASYNC_OPFLAGS_CALLBACK          0x00000200
 67 mday          1.20 #define ASYNC_OPFLAGS_FORWARD           0x00000400
 68 mday          1.24 #define ASYNC_OPFLAGS_PSEUDO_CALLBACK   0x00000800
 69 mday          1.26 #define ASYNC_OPFLAGS_SAFE_CALLBACK     0x00001000
 70 mike          1.2  
 71                    #define ASYNC_OPSTATE_UNKNOWN           0x00000000
 72                    #define ASYNC_OPSTATE_OFFERED           0x00000001
 73                    #define ASYNC_OPSTATE_DECLINED          0x00000002
 74                    #define ASYNC_OPSTATE_STARTED           0x00000004
 75                    #define ASYNC_OPSTATE_PROCESSING        0x00000008
 76 chip          1.30 #define ASYNC_OPSTATE_DELIVER           0x00000010
 77 mike          1.2  #define ASYNC_OPSTATE_RESERVE           0x00000020
 78                    #define ASYNC_OPSTATE_COMPLETE          0x00000040
 79                    #define ASYNC_OPSTATE_TIMEOUT           0x00000080
 80                    #define ASYNC_OPSTATE_CANCELLED         0x00000100
 81                    #define ASYNC_OPSTATE_PAUSED            0x00000200
 82 mday          1.12 #define ASYNC_OPSTATE_SUSPENDED         0x00000400
 83 mday          1.3  #define ASYNC_OPSTATE_RESUMED           0x00000800
 84 mday          1.8  #define ASYNC_OPSTATE_ORPHANED          0x00001000
 85 mday          1.12 #define ASYNC_OPSTATE_RELEASED          0x00002000
 86 mike          1.2  
 87 mday          1.8  class Cimom;
 88 mday          1.23 class Thread;
 89 mday          1.13 
 90 mike          1.2  class PEGASUS_COMMON_LINKAGE AsyncOpNode
 91                    {
 92 mday          1.26 
 93 mday          1.38 //     public:
 94                    //       static void * operator new(size_t );
 95                    //       static void operator delete( void *, size_t);
 96                    //    private:
 97                    //       static AsyncOpNode * _headOfFreeList;
 98                    //       static const int BLOCK_SIZE;
 99                    //       static Mutex _alloc_mut;
100 mike          1.2     public:
101                    
102                          AsyncOpNode(void);
103                          ~AsyncOpNode(void);
104 chip          1.30 
105 mike          1.2        Boolean  operator == (const void *key) const;
106                          Boolean operator == (const AsyncOpNode & node) const;
107 mday          1.9  
108 david.dillard 1.45       void get_timeout_interval(struct timeval *buffer);
109 mday          1.9        void set_timeout_interval(const struct timeval *interval);
110 chip          1.30 
111 david.dillard 1.45       Boolean timeout(void);
112 mike          1.2  
113 david.dillard 1.45       OperationContext & get_context(void);
114 mike          1.2  
115 david.dillard 1.45       void put_request(const Message *request);
116 mday          1.13       Message *get_request(void) ;
117 chip          1.30 
118 david.dillard 1.45       void put_response(const Message *response);
119 mday          1.13       Message *get_response(void) ;
120 chip          1.30 
121 david.dillard 1.45       Uint32 read_state(void);
122                          void write_state(Uint32);
123 chip          1.30 
124 mike          1.2        Uint32 read_flags(void);
125                          void write_flags(Uint32);
126 chip          1.30 
127 david.dillard 1.45       /**
128                            @exception  IPCException    Indicates an IPC error occurred.
129                          */
130                          void lock(void);
131                    
132                          /**
133                            @exception  IPCException    Indicates an IPC error occurred.
134                          */
135                          void unlock(void);
136                    
137                          /**
138                            @exception  IPCException    Indicates an IPC error occurred.
139                          */
140                          void udpate(void);
141                    
142                          /**
143                            @exception  IPCException    Indicates an IPC error occurred.
144                          */
145                          void deliver(const Uint32 count);
146                    
147                          /**
148 david.dillard 1.45         @exception  IPCException    Indicates an IPC error occurred.
149                          */
150                          void reserve(const Uint32 size);
151                    
152                          /**
153                            @exception  IPCException    Indicates an IPC error occurred.
154                          */
155                          void processing(void);
156                    
157                          /**
158                            @exception  IPCException    Indicates an IPC error occurred.
159                          */
160                          void processing(OperationContext *context);
161                    
162                          /**
163                            @exception  IPCException    Indicates an IPC error occurred.
164                          */
165                          void complete(void);
166                    
167                          /**
168                            @exception  IPCException    Indicates an IPC error occurred.
169 david.dillard 1.45       */
170                          void complete(OperationContext *context);
171 mday          1.12       void release(void);
172 mday          1.10       void wait(void);
173 chip          1.30 
174                    
175 mike          1.2     private:
176                          Semaphore _client_sem;
177                          Mutex _mut;
178 mday          1.16       unlocked_dq<Message> _request;
179 chip          1.30       unlocked_dq<Message> _response;
180                    
181 mike          1.2        OperationContext _operation_list;
182                          Uint32 _state;
183                          Uint32 _flags;
184                          Uint32 _offered_count;
185                          Uint32 _total_ops;
186                          Uint32 _completed_ops;
187 mday          1.15       Uint32 _user_data;
188 mday          1.17       Uint32 _completion_code;
189 mday          1.19       MessageQueue *_op_dest;
190 chip          1.30 
191 mike          1.2        struct timeval _start;
192                          struct timeval _lifetime;
193                          struct timeval _updated;
194                          struct timeval _timeout_interval;
195                    
196                          AsyncOpNode *_parent;
197                          unlocked_dq<AsyncOpNode> _children;
198                    
199                          void _reset(unlocked_dq<AsyncOpNode> *dst_q);
200                    
201 mday          1.9        // the lifetime member is for cache management by the cimom
202 mike          1.2        void _set_lifetime(struct timeval *lifetime) ;
203 mday          1.12       Boolean _check_lifetime(void) ;
204 mike          1.2  
205                          Boolean _is_child(void) ;
206                          Uint32 _is_parent(void) ;
207                          Boolean _is_my_child(const AsyncOpNode & caller) const;
208                          void _make_orphan( AsyncOpNode & parent) ;
209                          void _adopt_child(AsyncOpNode *child) ;
210                          void _disown_child(AsyncOpNode *child) ;
211 chip          1.30       void (*_async_callback)(AsyncOpNode *,
212 david.dillard 1.45                   MessageQueue *,
213                                      void *);
214 mday          1.26       void (*__async_callback)(Message *, void *, void *);
215 mday          1.23       // << Tue Mar 12 14:44:51 2002 mdd >>
216 chip          1.30       // pointers for async callbacks  - don't use
217 mday          1.19       AsyncOpNode *_callback_node;
218 mday          1.21       MessageQueue *_callback_response_q;
219 mday          1.19       void *_callback_ptr;
220 mday          1.26       void *_callback_parameter;
221                          void *_callback_handle;
222 mday          1.28       Condition *_callback_notify;
223 chip          1.30 
224 mday          1.22       MessageQueue *_callback_request_q;
225 mday          1.23       //      << Tue Mar 12 14:44:53 2002 mdd >>
226 chip          1.30       // pointers to help static class message handlers - don't use
227 mday          1.23       MessageQueue *_service_ptr;
228                          Thread *_thread_ptr;
229 chip          1.30 
230 mday          1.8        friend class cimom;
231 mday          1.10       friend class MessageQueueService;
232 mday          1.29       friend class ProviderManagerService;
233 mday          1.37       friend class BinaryMessageHandler;
234 mday          1.32  public:
235                          // << Tue Jun  4 16:44:09 2002 mdd >>
236 david.dillard 1.45       // debug artifact
237 mday          1.32       Uint32 _source_queue;
238 mday          1.34       // << Fri Jul 19 08:41:45 2002 mdd >>
239                          // debugging utility
240                          // careful - wipes out current value of *buf
241 david.dillard 1.43       void print_to_buffer(char **buf);
242 a.arora       1.40       String print_to_string(void);
243 mike          1.2  };
244                    
245                    
246                    inline Boolean AsyncOpNode::operator == (const void *key) const
247                    {
248                       if (key == (void *)this)
249                          return true;
250                       return false;
251                    }
252                    
253                    inline Boolean AsyncOpNode::operator == (const AsyncOpNode & node) const
254                    {
255                       return AsyncOpNode::operator==((const void *)&node);
256                    }
257                    
258 mday          1.9  
259 chip          1.30 inline void AsyncOpNode::get_timeout_interval(struct timeval *buffer)
260 mday          1.9  {
261                       if(buffer != 0)
262                       {
263 a.arora       1.41       AutoMutex autoMut(_mut);
264 mday          1.9        buffer->tv_sec = _timeout_interval.tv_sec;
265                          buffer->tv_usec = _timeout_interval.tv_usec;
266 a.arora       1.41    } // mutex unlocks here
267 mday          1.9  }
268                    
269                    inline void AsyncOpNode::set_timeout_interval(const struct timeval *interval)
270                    {
271                       if(interval != 0)
272                       {
273 a.arora       1.41       AutoMutex autoMut(_mut);
274 mday          1.9        _timeout_interval.tv_sec = interval->tv_sec;
275                          _timeout_interval.tv_usec = interval->tv_usec;
276 mday          1.12       gettimeofday(&_updated, NULL);
277 a.arora       1.41    } // mutex unlocks here
278 mday          1.9  }
279                    
280                    
281 chip          1.30 inline Boolean AsyncOpNode::timeout(void)
282 mike          1.2  {
283                       struct timeval now;
284                       gettimeofday(&now, NULL);
285 mday          1.12    Boolean ret = false;
286 david.dillard 1.45 
287 a.arora       1.41    AutoMutex autoMut(_mut);
288 mday          1.15    if((_updated.tv_sec + _timeout_interval.tv_sec ) < now.tv_sec)
289 a.arora       1.41        if((_updated.tv_usec + _timeout_interval.tv_usec ) < now.tv_usec)
290                        ret =  true;
291 david.dillard 1.45 
292 mday          1.12    return ret;
293 mike          1.2  }
294                    
295 mday          1.4  // context is now a locked list
296 mday          1.3  inline OperationContext & AsyncOpNode::get_context(void)
297 mike          1.2  {
298 mday          1.9     gettimeofday(&_updated, NULL);
299 mike          1.2     return _operation_list;
300                    }
301                    
302 mday          1.16 
303 chip          1.30 inline  void AsyncOpNode::put_request(const Message *request)
304 mike          1.2  {
305 a.arora       1.41    AutoMutex autoMut(_mut);
306 mday          1.9     gettimeofday(&_updated, NULL);
307 mday          1.21    if( false == _request.exists(reinterpret_cast<void *>(const_cast<Message *>(request))) )
308 mday          1.16    _request.insert_last( const_cast<Message *>(request) ) ;
309 mday          1.15 
310 mday          1.16 //   _request = const_cast<Message *>(request);
311 mike          1.2  }
312                    
313 chip          1.30 inline Message * AsyncOpNode::get_request(void)
314 mike          1.2  {
315 mday          1.12    Message *ret;
316 a.arora       1.41    AutoMutex autoMut(_mut);
317 mday          1.9     gettimeofday(&_updated, NULL);
318 mday          1.16    ret = _request.remove_first() ;
319 a.arora       1.41 
320 mday          1.12    return ret;
321 mike          1.2  }
322                    
323 chip          1.30 inline void AsyncOpNode::put_response(const Message *response)
324 mike          1.2  {
325 a.arora       1.41    AutoMutex autoMut(_mut);
326 mday          1.9     gettimeofday(&_updated, NULL);
327 mday          1.21    if (false == _response.exists(reinterpret_cast<void *>(const_cast<Message *>(response))))
328 mday          1.16    _response.insert_last( const_cast<Message *>(response) );
329 mike          1.2  }
330                    
331 chip          1.30 inline Message * AsyncOpNode::get_response(void)
332 mike          1.2  {
333 mday          1.12    Message *ret;
334                    
335 a.arora       1.41    AutoMutex autoMut(_mut);
336 mday          1.15 //   gettimeofday(&_updated, NULL);
337 mday          1.16    ret = _response.remove_first();
338                    //   ret = _response;
339 chip          1.30 
340 mday          1.12    return ret;
341 mike          1.2  }
342                    
343                    inline Uint32 AsyncOpNode::read_state(void)
344                    {
345 a.arora       1.41    AutoMutex autoMut(_mut);
346 mday          1.9     gettimeofday(&_updated, NULL);
347 mday          1.12    Uint32 ret = _state;
348 david.dillard 1.45 
349 mday          1.12    return ret;
350 chip          1.30 
351 mike          1.2  }
352                    
353                    inline void AsyncOpNode::write_state(Uint32 state)
354                    {
355 a.arora       1.41    AutoMutex autoMut(_mut);
356 mday          1.9     gettimeofday(&_updated, NULL);
357 mike          1.2     _state = state;
358                    }
359                    
360                    inline Uint32 AsyncOpNode::read_flags(void)
361                    {
362 a.arora       1.41    AutoMutex autoMut(_mut);
363 mday          1.9     gettimeofday(&_updated, NULL);
364 mday          1.12    Uint32 ret = _flags;
365 david.dillard 1.45 
366 mday          1.12    return ret;
367 mike          1.2  }
368                    
369                    inline void AsyncOpNode::write_flags(Uint32 flags)
370 chip          1.30 {
371 a.arora       1.41    AutoMutex autoMut(_mut);
372 mday          1.9     gettimeofday(&_updated, NULL);
373 mike          1.2     _flags = flags;
374                    }
375                    
376                    
377 chip          1.30 inline  void AsyncOpNode::lock(void)
378 mike          1.2  {
379                       _mut.lock(pegasus_thread_self());
380                    }
381                    
382 chip          1.30 inline void AsyncOpNode::unlock(void)
383 mike          1.2  {
384                       _mut.unlock();
385                    }
386                    
387                    inline void AsyncOpNode::udpate(void)
388                    {
389 a.arora       1.41    AutoMutex autoMut(_mut);
390 mike          1.2     gettimeofday(&_updated, NULL);
391                    }
392                    
393 chip          1.30 inline void AsyncOpNode::deliver(const Uint32 count)
394 mike          1.2  {
395 a.arora       1.41    AutoMutex autoMut(_mut);
396 mike          1.2     _completed_ops = count;
397 mday          1.15    _state |= ASYNC_OPSTATE_DELIVER;
398 mike          1.2     gettimeofday(&_updated, NULL);
399                    }
400                    
401                    inline void AsyncOpNode::reserve(const Uint32 size)
402                    {
403 a.arora       1.41    AutoMutex autoMut(_mut);
404 mike          1.2     _total_ops = size;
405 mday          1.15    _state |= ASYNC_OPSTATE_RESERVE;
406 mike          1.2     gettimeofday(&_updated, NULL);
407                    }
408                    
409 chip          1.30 inline void AsyncOpNode::processing(void)
410 mike          1.2  {
411 a.arora       1.41    AutoMutex autoMut(_mut);
412 mday          1.15    _state |= ASYNC_OPSTATE_PROCESSING;
413 mike          1.2     gettimeofday(&_updated, NULL);
414                    }
415                    
416                    // con will be empty upon return of this member function
417 chip          1.30 inline void AsyncOpNode::processing(OperationContext *con)
418 mike          1.2  {
419 a.arora       1.41    AutoMutex autoMut(_mut);
420 mday          1.15    _state |= ASYNC_OPSTATE_PROCESSING;
421 mike          1.2     gettimeofday(&_updated, NULL);
422 chip          1.30 
423                       /*
424 mike          1.2     context *c = con->remove_context();
425                       while(c != 0)
426                       {
427                          _operation_list.add_context(c);
428                          c = con->remove_context();
429                       }
430 chip          1.30    */
431 mike          1.2  }
432                    
433 chip          1.30 inline void AsyncOpNode::complete(void)
434 mike          1.2  {
435 a.arora       1.41    AutoMutex autoMut(_mut);
436 mday          1.15    _state |= ASYNC_OPSTATE_COMPLETE;
437 mike          1.2     gettimeofday(&_updated, NULL);
438                    }
439                    
440                    inline void AsyncOpNode::complete(OperationContext *con)
441                    {
442 a.arora       1.41    AutoMutex autoMut(_mut);
443 mday          1.15    _state |= ASYNC_OPSTATE_COMPLETE;
444 mike          1.2     gettimeofday(&_updated, NULL);
445 chip          1.30    /*
446 mike          1.2     context *c = con->remove_context();
447                       while(c != 0)
448                       {
449                          _operation_list.add_context(c);
450                          c = con->remove_context();
451                       }
452 chip          1.30    */
453 mday          1.10 }
454                    
455                    inline void AsyncOpNode::wait(void)
456                    {
457                       _client_sem.wait();
458 mike          1.2  }
459                    
460 mday          1.12 inline void AsyncOpNode::release(void)
461                    {
462 a.arora       1.41    AutoMutex autoMut(_mut);
463 mday          1.12    _state |= ASYNC_OPSTATE_RELEASED;
464                    }
465                    
466 chip          1.30 inline  void AsyncOpNode::_set_lifetime(struct timeval *lifetime)
467 mike          1.2  {
468 a.arora       1.41    AutoMutex autoMut(_mut);
469 mike          1.2     _lifetime.tv_sec = lifetime->tv_sec;
470                       _lifetime.tv_usec = lifetime->tv_usec;
471 a.arora       1.41 
472 mike          1.2  }
473                    
474 chip          1.30 inline Boolean AsyncOpNode::_check_lifetime(void)
475 mike          1.2  {
476                       struct timeval now;
477 chip          1.30 
478 mike          1.2     gettimeofday(&now, NULL);
479                       if((_start.tv_sec + _lifetime.tv_sec ) >= now.tv_sec)
480                          if((_start.tv_usec + _lifetime.tv_usec ) >= now.tv_usec)
481 david.dillard 1.45             return true;
482 mike          1.2     return false;
483                    }
484                    
485                    inline Boolean AsyncOpNode::_is_child(void)
486                    {
487                       if (_parent != 0)
488                          return true;
489                       return false;
490                    }
491                    
492                    inline Uint32 AsyncOpNode::_is_parent(void)
493                    {
494                       return _children.count();
495                    }
496                    
497                    inline Boolean AsyncOpNode::_is_my_child(const AsyncOpNode & caller) const
498 chip          1.30 {
499 mike          1.2     if ( _parent == &caller )
500                          return true;
501                       return false;
502                    }
503                    
504 chip          1.30 inline void AsyncOpNode::_make_orphan( AsyncOpNode & parent)
505 mike          1.2  {
506                       if( _parent == &parent )
507                       {
508                          _parent = NULL;
509                          parent._children.remove(this);
510                       }
511                       else
512                          throw Permission(pegasus_thread_self());
513                    }
514                    
515 chip          1.30 inline void AsyncOpNode::_adopt_child(AsyncOpNode *child)
516 mike          1.2  {
517                       if(child == NULL)
518                          throw NullPointer();
519                       if(true == child->_is_child())
520                          throw Permission(pegasus_thread_self());
521                       child->_parent = this;
522                       _children.insert_last(child);
523                    }
524 chip          1.30 
525 mike          1.2  inline void AsyncOpNode::_disown_child(AsyncOpNode *child)
526                    {
527                       if(child == NULL)
528                          throw NullPointer();
529                       if( false == child->_is_child() || false == child->_is_my_child( *this ))
530                          throw Permission(pegasus_thread_self());
531                       child->_make_orphan( *this );
532                       _children.remove(child);
533 chip          1.30 }
534 mike          1.2  
535                    PEGASUS_NAMESPACE_END
536                    
537                    #endif //Pegasus_AsyncOpNode_h

No CVS admin address has been configured
Powered by
ViewCVS 0.9.2