Index: XIOS/trunk/src/filter/filter.cpp
===================================================================
--- XIOS/trunk/src/filter/filter.cpp	(revision 1003)
+++ XIOS/trunk/src/filter/filter.cpp	(revision 1006)
@@ -5,5 +5,5 @@
   CFilter::CFilter(CGarbageCollector& gc, size_t inputSlotsCount, IFilterEngine* engine)
     : CInputPin(gc, inputSlotsCount)
-    , COutputPin()
+    , COutputPin(gc)
     , engine(engine)
     , inputSlotCount(inputSlotCount)
@@ -14,5 +14,29 @@
     CDataPacketPtr outputPacket = engine->apply(data);
     if (outputPacket)
-      deliverOuput(outputPacket);
+      onOutputReady(outputPacket);
+  }
+
+  void CFilter::setInputTrigger(size_t inputSlot, COutputPin* trigger)
+  {
+    // Was the filter already triggerable? If not, we need to inform
+    // all downstream filters.
+    bool wasTriggerable = canBeTriggered();
+
+    CInputPin::setInputTrigger(inputSlot, trigger);
+
+    if (!wasTriggerable)
+      setOutputTriggers();
+  }
+
+  void CFilter::trigger(Time timestamp)
+  {
+    CInputPin::trigger(timestamp);
+
+    COutputPin::trigger(timestamp);
+  }
+
+  bool CFilter::canBeTriggered() const
+  {
+    return (CInputPin::canBeTriggered() || COutputPin::canBeTriggered());
   }
 } // namespace xios
Index: XIOS/trunk/src/filter/filter.hpp
===================================================================
--- XIOS/trunk/src/filter/filter.hpp	(revision 1003)
+++ XIOS/trunk/src/filter/filter.hpp	(revision 1006)
@@ -26,4 +26,26 @@
       CFilter(CGarbageCollector& gc, size_t inputSlotsCount, IFilterEngine* engine);
 
+      /*!
+       * Sets the trigger for a specific input slot.
+       *
+       * \param inputSlot the input slot number
+       * \param trigger the corresponding trigger
+       */
+      void virtual setInputTrigger(size_t inputSlot, COutputPin* trigger);
+
+      /*!
+       * Triggers the filter for the specified timestamp.
+       *
+       * \param timestamp the timestamp for which we are triggering the filter
+       */
+      void virtual trigger(Time timestamp);
+
+      /*!
+       * Tests if the filter can be triggered.
+       *
+       * \return true if the filter can be triggered
+       */
+      bool virtual canBeTriggered() const;
+
     protected:
       IFilterEngine* engine; //!< The filter engine, might be the filter itself
Index: XIOS/trunk/src/filter/garbage_collector.cpp
===================================================================
--- XIOS/trunk/src/filter/garbage_collector.cpp	(revision 1003)
+++ XIOS/trunk/src/filter/garbage_collector.cpp	(revision 1006)
@@ -3,28 +3,28 @@
 namespace xios
 {
-  void CGarbageCollector::registerFilter(CInputPin* inputPin, Time timestamp)
+  void CGarbageCollector::registerObject(InvalidableObject* Object, Time timestamp)
   {
-    registeredFilters[timestamp].insert(inputPin);
+    registeredObjects[timestamp].insert(Object);
   }
 
-  void CGarbageCollector::unregisterFilter(CInputPin* inputPin, Time timestamp)
+  void CGarbageCollector::unregisterObject(InvalidableObject* Object, Time timestamp)
   {
-    std::map<Time, std::set<CInputPin*> >::iterator it = registeredFilters.find(timestamp);
-    if (it != registeredFilters.end())
-      it->second.erase(inputPin);
+    std::map<Time, std::set<InvalidableObject*> >::iterator it = registeredObjects.find(timestamp);
+    if (it != registeredObjects.end())
+      it->second.erase(Object);
   }
 
   void CGarbageCollector::invalidate(Time timestamp)
   {
-    std::map<Time, std::set<CInputPin*> >::iterator it    = registeredFilters.begin(),
-                                                    itEnd = registeredFilters.lower_bound(timestamp);
+    std::map<Time, std::set<InvalidableObject*> >::iterator it    = registeredObjects.begin(),
+                                                            itEnd = registeredObjects.lower_bound(timestamp);
     for (; it != itEnd; ++it)
     {
-      std::set<CInputPin*>::iterator itFilter    = it->second.begin(),
-                                     itFilterEnd = it->second.end();
-      for (; itFilter != itFilterEnd; ++itFilter)
-        (*itFilter)->invalidate(timestamp);
+      std::set<InvalidableObject*>::iterator itObject    = it->second.begin(),
+                                             itObjectEnd = it->second.end();
+      for (; itObject != itObjectEnd; ++itObject)
+        (*itObject)->invalidate(timestamp);
     }
-    registeredFilters.erase(registeredFilters.begin(), itEnd);
+    registeredObjects.erase(registeredObjects.begin(), itEnd);
   }
 } // namespace xios
Index: XIOS/trunk/src/filter/garbage_collector.hpp
===================================================================
--- XIOS/trunk/src/filter/garbage_collector.hpp	(revision 1003)
+++ XIOS/trunk/src/filter/garbage_collector.hpp	(revision 1006)
@@ -5,8 +5,21 @@
 #include <set>
 
-#include "input_pin.hpp"
+#include "date.hpp"
 
 namespace xios
 {
+  /*!
+   * Interface shared by all objects that might need to invalidate packets.
+   */
+  struct InvalidableObject
+  {
+    /*!
+     * Removes all pending packets which are older than the specified timestamp.
+     *
+     * \param timestamp the timestamp used for invalidation
+     */
+    void virtual invalidate(Time timestamp) = 0;
+  }; // struct InvalidableObject
+
   /*!
    * A basic garbage collector which ensures no old packets linger in the filter graph.
@@ -22,21 +35,21 @@
 
       /*!
-       * Registers a filter for a specified timestamp.
+       * Registers an object for a specified timestamp.
        *
-       * \param inputPin the input pin of the filter to register
-       * \param timestamp the timestamp for which the filter is registered
+       * \param object the object to register
+       * \param timestamp the timestamp for which the object is registered
        */
-      void registerFilter(CInputPin* inputPin, Time timestamp);
+      void registerObject(InvalidableObject* object, Time timestamp);
 
       /*!
-       * Removes a filter previously registered for a specified timestamp.
+       * Removes a object previously registered for a specified timestamp.
        *
-       * \param inputPin the input pin of the filter to unregister
-       * \param timestamp the timestamp for which the filter is unregistered
+       * \param object the object to unregister
+       * \param timestamp the timestamp for which the object is unregistered
        */
-      void unregisterFilter(CInputPin* inputPin, Time timestamp);
+      void unregisterObject(InvalidableObject* object, Time timestamp);
 
       /*!
-       * Ensures all registered filters invalidate packets older than the specified timestamp.
+       * Ensures all registered objects invalidate packets older than the specified timestamp.
        *
        * \param timestamp the timestamp used for invalidation
@@ -48,5 +61,5 @@
       CGarbageCollector& operator=(const CGarbageCollector&);
 
-      std::map<Time, std::set<CInputPin*> > registeredFilters; //!< Currently registered filters
+      std::map<Time, std::set<InvalidableObject*> > registeredObjects; //!< Currently registered objects
   }; // class CGarbageCollector
 } // namespace xios
Index: XIOS/trunk/src/filter/input_pin.cpp
===================================================================
--- XIOS/trunk/src/filter/input_pin.cpp	(revision 1003)
+++ XIOS/trunk/src/filter/input_pin.cpp	(revision 1006)
@@ -1,3 +1,4 @@
 #include "input_pin.hpp"
+#include "output_pin.hpp"
 #include "garbage_collector.hpp"
 #include "exception.hpp"
@@ -8,4 +9,6 @@
     : gc(gc)
     , slotsCount(slotsCount)
+    , triggers(slotsCount)
+    , hasTriggers(false)
   { /* Nothing to do */ }
 
@@ -23,5 +26,5 @@
     {
       it = inputs.insert(std::make_pair(packet->timestamp, InputBuffer(slotsCount))).first;
-      gc.registerFilter(this, packet->timestamp);
+      gc.registerObject(this, packet->timestamp);
     }
     it->second.slotsFilled++;
@@ -31,8 +34,41 @@
     {
       // Unregister before calling onInputReady in case the filter registers again
-      gc.unregisterFilter(this, packet->timestamp);
+      gc.unregisterObject(this, packet->timestamp);
       onInputReady(it->second.packets);
       inputs.erase(it);
     }
+  }
+
+  void CInputPin::setInputTrigger(size_t inputSlot, COutputPin* trigger)
+  {
+    if (inputSlot >= slotsCount)
+      ERROR("void CInputPin::setInputTrigger(size_t inputSlot, COutputPin* trigger)",
+            "The input slot " << inputSlot << " does not exist.");
+    if (triggers[inputSlot])
+      ERROR("void CInputPin::setInputTrigger(size_t inputSlot, COutputPin* trigger)",
+            "The trigger for input slot " << inputSlot << " has already been set.");
+
+    triggers[inputSlot] = trigger;
+    hasTriggers = true;
+  }
+
+  void CInputPin::trigger(Time timestamp)
+  {
+    if (hasTriggers) // Don't use canBeTriggered here, this function is virtual and can be overriden
+    {
+      std::map<Time, InputBuffer>::iterator it = inputs.find(timestamp);
+      bool nothingReceived = (it == inputs.end());
+
+      for (size_t s = 0; s < slotsCount; s++)
+      {
+        if (triggers[s] && (nothingReceived || !it->second.packets[s]))
+          triggers[s]->trigger(timestamp);
+      }
+    }
+  }
+
+  bool CInputPin::canBeTriggered() const
+  {
+    return hasTriggers;
   }
 
Index: XIOS/trunk/src/filter/input_pin.hpp
===================================================================
--- XIOS/trunk/src/filter/input_pin.hpp	(revision 1003)
+++ XIOS/trunk/src/filter/input_pin.hpp	(revision 1006)
@@ -5,14 +5,15 @@
 #include <map>
 
+#include "garbage_collector.hpp"
 #include "data_packet.hpp"
 
 namespace xios
 {
-  class CGarbageCollector;
+  class COutputPin;
 
   /*!
    * An input pin handles the data packets received by a filter.
    */
-  class CInputPin
+  class CInputPin : public InvalidableObject
   {
     public:
@@ -27,4 +28,12 @@
 
       /*!
+       * Sets the trigger for a specific input slot.
+       *
+       * \param inputSlot the input slot number
+       * \param trigger the corresponding trigger
+       */
+      void virtual setInputTrigger(size_t inputSlot, COutputPin* trigger);
+
+      /*!
        * Receives a data packet from an upstream filter on
        * the specified input slot.
@@ -37,4 +46,18 @@
 
       /*!
+       * Triggers the input of any buffered packet for the specified timestamp.
+       *
+       * \param timestamp the timestamp for which we are triggering the input
+       */
+      void virtual trigger(Time timestamp);
+
+      /*!
+       * Tests if the pin can be triggered.
+       *
+       * \return true if the pin can be triggered
+       */
+      bool virtual canBeTriggered() const;
+
+      /*!
        * Removes all pending packets which are older than the specified timestamp.
        *
@@ -44,6 +67,4 @@
 
     protected:
-      CGarbageCollector& gc; //!< The garbage collector associated to the input pin
-
       /*!
        * Function triggered when all slots have been filled for a specific timestamp.
@@ -75,8 +96,16 @@
       };
 
+      CGarbageCollector& gc; //!< The garbage collector associated to the input pin
+
       size_t slotsCount; //!< The number of slots
 
       //! Input buffer, store the packets until all slots are full for a timestep
       std::map<Time, InputBuffer> inputs;
+
+      //! Store the triggers corresponding to the input slots
+      std::vector<COutputPin*> triggers;
+
+      //! Whether some triggers have been set
+      bool hasTriggers;
   }; // class CInputPin
 } // namespace xios
Index: XIOS/trunk/src/filter/output_pin.cpp
===================================================================
--- XIOS/trunk/src/filter/output_pin.cpp	(revision 1003)
+++ XIOS/trunk/src/filter/output_pin.cpp	(revision 1006)
@@ -4,4 +4,9 @@
 namespace xios
 {
+  COutputPin::COutputPin(CGarbageCollector& gc, bool manualTrigger /*= false*/)
+    : gc(gc)
+    , manualTrigger(manualTrigger)
+  { /* Nothing to do */ }
+
   void COutputPin::connectOutput(boost::shared_ptr<CInputPin> inputPin, size_t inputSlot)
   {
@@ -11,4 +16,22 @@
 
     outputs.push_back(std::make_pair(inputPin, inputSlot));
+
+    if (canBeTriggered())
+      inputPin->setInputTrigger(inputSlot, this);
+  }
+
+  void COutputPin::onOutputReady(CDataPacketPtr packet)
+  {
+    if (!packet)
+      ERROR("void COutputPin::onOutputReady(CDataPacketPtr packet)",
+            "The packet cannot be null.");
+
+    if (manualTrigger) // Don't use canBeTriggered here, this function is virtual and can be overriden
+    {
+      outputPackets[packet->timestamp] = packet;
+      gc.registerObject(this, packet->timestamp);
+    }
+    else
+      deliverOuput(packet);
   }
 
@@ -23,3 +46,34 @@
       it->first->setInput(it->second, packet);
   }
+
+  void COutputPin::trigger(Time timestamp)
+  {
+    if (manualTrigger) // Don't use canBeTriggered here, this function is virtual and can be overriden
+    {
+      std::map<Time, CDataPacketPtr>::iterator it = outputPackets.find(timestamp);
+      if (it != outputPackets.end())
+      {
+        gc.unregisterObject(this, timestamp);
+        deliverOuput(it->second);
+        outputPackets.erase(it);
+      }
+    }
+  }
+
+  bool COutputPin::canBeTriggered() const
+  {
+    return manualTrigger;
+  }
+
+  void COutputPin::setOutputTriggers()
+  {
+    std::vector<std::pair<boost::shared_ptr<CInputPin>, size_t> >::iterator it, itEnd;
+    for (it = outputs.begin(), itEnd = outputs.end(); it != itEnd; ++it)
+      it->first->setInputTrigger(it->second, this);
+  }
+
+  void COutputPin::invalidate(Time timestamp)
+  {
+    outputPackets.erase(outputPackets.begin(), outputPackets.lower_bound(timestamp));
+  }
 } // namespace xios
Index: XIOS/trunk/src/filter/output_pin.hpp
===================================================================
--- XIOS/trunk/src/filter/output_pin.hpp	(revision 1003)
+++ XIOS/trunk/src/filter/output_pin.hpp	(revision 1006)
@@ -2,4 +2,5 @@
 #define __XIOS_COutputPin__
 
+#include "garbage_collector.hpp"
 #include "input_pin.hpp"
 
@@ -9,7 +10,16 @@
    * An output pin handles the connections with downstream filters.
    */
-  class COutputPin
+  class COutputPin : public InvalidableObject
   {
     public:
+      /*!
+       * Constructs an ouput pin with manual or automatic trigger
+       * and an associated garbage collector.
+       *
+       * \param gc the garbage collector associated with this ouput pin
+       * \param slotsCount the number of slots
+       */
+      COutputPin(CGarbageCollector& gc, bool manualTrigger = false);
+
       /*!
        * Connects to a specific slot of the input pin of a downstream filter.
@@ -21,5 +31,39 @@
       void connectOutput(boost::shared_ptr<CInputPin> inputPin, size_t inputSlot);
 
+      /*!
+       * Triggers the output of any buffered packet for the specified timestamp.
+       *
+       * \param timestamp the timestamp for which we are triggering the output
+       */
+      void virtual trigger(Time timestamp);
+
+      /*!
+       * Tests if the pin can be triggered.
+       *
+       * \return true if the pin can be triggered
+       */
+      bool virtual canBeTriggered() const;
+
+      /*!
+       * Removes all pending packets which are older than the specified timestamp.
+       *
+       * \param timestamp the timestamp used for invalidation
+       */
+      void virtual invalidate(Time timestamp);
+
     protected:
+      /*!
+       * Function triggered when a packet is ready to be delivered.
+       *
+       * \param packet the packet ready for output
+       */
+      void onOutputReady(CDataPacketPtr packet);
+
+      /*!
+       * Informs the downstream pins that this output pin should be triggered.
+       */
+      void setOutputTriggers();
+
+    private:
       /*!
        * Delivers an output packet to the downstreams filter.
@@ -29,7 +73,14 @@
       void deliverOuput(CDataPacketPtr packet);
 
-    private:
+      CGarbageCollector& gc; //!< The garbage collector associated to the output pin
+
+      //!< Whether the ouput should be triggered manually
+      bool manualTrigger;
+
       //!< The list of connected filters and the corresponding slot numbers
       std::vector<std::pair<boost::shared_ptr<CInputPin>, size_t> > outputs;
+
+      //! Output buffer, store the packets until the output is triggered
+      std::map<Time, CDataPacketPtr> outputPackets;
   }; // class COutputPin
 } // namespace xios
Index: XIOS/trunk/src/filter/source_filter.cpp
===================================================================
--- XIOS/trunk/src/filter/source_filter.cpp	(revision 1003)
+++ XIOS/trunk/src/filter/source_filter.cpp	(revision 1006)
@@ -6,6 +6,8 @@
 namespace xios
 {
-  CSourceFilter::CSourceFilter(CGrid* grid, const CDuration offset /*= NoneDu*/)
-    : grid(grid)
+  CSourceFilter::CSourceFilter(CGarbageCollector& gc, CGrid* grid,
+                               const CDuration offset /*= NoneDu*/, bool manualTrigger /*= false*/)
+    : COutputPin(gc, manualTrigger)
+    , grid(grid)
     , offset(offset)
   {
@@ -28,5 +30,5 @@
     grid->inputField(data, packet->data);
 
-    deliverOuput(packet);
+    onOutputReady(packet);
   }
 
@@ -64,5 +66,5 @@
     }
 
-    deliverOuput(packet);
+    onOutputReady(packet);
   }
 
@@ -73,5 +75,5 @@
     packet->timestamp = date;
     packet->status = CDataPacket::END_OF_STREAM;
-    deliverOuput(packet);
+    onOutputReady(packet);
   }
 } // namespace xios
Index: XIOS/trunk/src/filter/source_filter.hpp
===================================================================
--- XIOS/trunk/src/filter/source_filter.hpp	(revision 1003)
+++ XIOS/trunk/src/filter/source_filter.hpp	(revision 1006)
@@ -19,8 +19,11 @@
        * Constructs a source filter accepting data attached to the specified grid.
        *
+       * \param gc the garbage collector associated with this filter
        * \param grid the grid to which the data is attached
        * \param offset the offset applied to the timestamp of all packets
+       * \param manualTrigger whether the output should be triggered manually
        */
-      CSourceFilter(CGrid* grid, const CDuration offset = NoneDu);
+      CSourceFilter(CGarbageCollector& gc, CGrid* grid,
+                    const CDuration offset = NoneDu, bool manualTrigger = false);
 
       /*!
Index: XIOS/trunk/src/filter/spatial_transform_filter.cpp
===================================================================
--- XIOS/trunk/src/filter/spatial_transform_filter.cpp	(revision 1003)
+++ XIOS/trunk/src/filter/spatial_transform_filter.cpp	(revision 1006)
@@ -53,5 +53,5 @@
     CDataPacketPtr outputPacket = spaceFilter->applyFilter(data, outputDefaultValue);
     if (outputPacket)
-      deliverOuput(outputPacket);
+      onOutputReady(outputPacket);
   }
 
Index: XIOS/trunk/src/filter/store_filter.cpp
===================================================================
--- XIOS/trunk/src/filter/store_filter.cpp	(revision 1003)
+++ XIOS/trunk/src/filter/store_filter.cpp	(revision 1006)
@@ -8,4 +8,5 @@
   CStoreFilter::CStoreFilter(CGarbageCollector& gc, CContext* context, CGrid* grid)
     : CInputPin(gc, 1)
+    , gc(gc)
     , context(context)
     , grid(grid)
@@ -27,4 +28,7 @@
     do
     {
+      if (canBeTriggered())
+        trigger(timestamp);
+
       timer.resume();
 
@@ -74,5 +78,5 @@
     // The packet is always destroyed by the garbage collector
     // so we register but never unregister
-    gc.registerFilter(this, data[0]->timestamp);
+    gc.registerObject(this, data[0]->timestamp);
   }
 
Index: XIOS/trunk/src/filter/store_filter.hpp
===================================================================
--- XIOS/trunk/src/filter/store_filter.hpp	(revision 1003)
+++ XIOS/trunk/src/filter/store_filter.hpp	(revision 1006)
@@ -65,4 +65,5 @@
 
     private:
+      CGarbageCollector& gc; //!< The garbage collector associated to the filter
       CContext* context; //!< The context to which the data belongs
       CGrid* grid; //!< The grid attached to the data the filter can accept
