[Aptitude-svn-commit] r4072 - in branches/aptitude-0.3/aptitude: . src/generic/problemresolver

Daniel Burrows dburrows at costa.debian.org
Sat Sep 10 23:35:12 UTC 2005


Author: dburrows
Date: Sat Sep 10 23:35:09 2005
New Revision: 4072

Modified:
   branches/aptitude-0.3/aptitude/ChangeLog
   branches/aptitude-0.3/aptitude/src/generic/problemresolver/exceptions.h
   branches/aptitude-0.3/aptitude/src/generic/problemresolver/problemresolver.h
Log:
Add hooks to manage the resolver in the presence of threads.

Modified: branches/aptitude-0.3/aptitude/ChangeLog
==============================================================================
--- branches/aptitude-0.3/aptitude/ChangeLog	(original)
+++ branches/aptitude-0.3/aptitude/ChangeLog	Sat Sep 10 23:35:09 2005
@@ -1,3 +1,13 @@
+2005-09-10  Daniel Burrows  <dburrows at debian.org>
+
+	* src/generic/problemresolver/exceptions.h, src/generic/problemresolver/problemresolver.h:
+
+	  Add code to manage the resolver in the presence of threads; it
+	  ensures that at most one find_next_solution call is running at
+	  once, and allows a find_next_solution call to be cleanly
+	  cancelled (throwing a special exception); it's guaranteed that
+	  you can start the resolver back up after cancelling it.
+
 2005-09-10 Jean-Luc Coulon  <jean-luc.coulon at wanadoo.fr>
 
 	* update French translation

Modified: branches/aptitude-0.3/aptitude/src/generic/problemresolver/exceptions.h
==============================================================================
--- branches/aptitude-0.3/aptitude/src/generic/problemresolver/exceptions.h	(original)
+++ branches/aptitude-0.3/aptitude/src/generic/problemresolver/exceptions.h	Sat Sep 10 23:35:09 2005
@@ -44,4 +44,24 @@
   }
 };
 
+/** An exception indicating that the resolver was interrupted from
+ *  another thread.
+ */
+class InterruptedException : public ProblemResolverError {
+  std::string errmsg() const
+  {
+    return "Dependency solution was interrupted.";
+  }
+};
+
+/** An exception indicating that two threads tried to run the resolver
+ *  at once.
+ */
+class DoubleRunException : public ProblemResolverError {
+  std::string errmsg() const
+  {
+    return "Internal error: Multiple threads of execution tried to enter the resolver at once.";
+  }
+};
+
 #endif // EXCEPTIONS_H

Modified: branches/aptitude-0.3/aptitude/src/generic/problemresolver/problemresolver.h
==============================================================================
--- branches/aptitude-0.3/aptitude/src/generic/problemresolver/problemresolver.h	(original)
+++ branches/aptitude-0.3/aptitude/src/generic/problemresolver/problemresolver.h	Sat Sep 10 23:35:09 2005
@@ -53,6 +53,7 @@
 #include "solution.h"
 
 #include "../dense_setset.h"
+#include "../threads.h"
 
 /** A dummy iterator that's always an "end" iterator. */
 template<class V>
@@ -187,64 +188,90 @@
     }
   };
 
-   typedef ExtractPackageId PackageHash;
+  typedef ExtractPackageId PackageHash;
 
-   /** Compares solutions according to their "goodness". */
-   struct solution_goodness_compare
-   {
-     bool operator()(const solution &s1, const solution &s2) const
-     {
-       return s1.get_score() < s2.get_score();
-     }
-   };
+  /** Compares solutions according to their "goodness". */
+  struct solution_goodness_compare
+  {
+    bool operator()(const solution &s1, const solution &s2) const
+    {
+      return s1.get_score() < s2.get_score();
+    }
+  };
 
-   /** Compares solutions according to their contents; used to
-    *  manage "closed".
-    */
-   struct solution_contents_compare
-   {
-     bool operator()(const solution &s1, const solution &s2) const
-     {
-       // Variation on lexicographical comparison.
-       //
-       // S1 < S2 if
-       //   - S1(p)=S2(p) for all p<p' and S1(p')<S2(p'), where
-       //     p,p' \in \dom(S1) \cup dom(S2)
+  /** Compares solutions according to their contents; used to
+   *  manage "closed".
+   */
+  struct solution_contents_compare
+  {
+    bool operator()(const solution &s1, const solution &s2) const
+    {
+      // Variation on lexicographical comparison.
+      //
+      // S1 < S2 if
+      //   - S1(p)=S2(p) for all p<p' and S1(p')<S2(p'), where
+      //     p,p' \in \dom(S1) \cup dom(S2)
 
 
-       // NB: the correctness of this code depends implicitly on the
-       // invariant that the score is a function of the contents of the
-       // solution.  Essentially we're using the score as a sort of
-       // hash value on solutions!
-       if(s1.get_score() < s2.get_score())
-	 return true;
-       else if(s2.get_score() < s1.get_score())
-	 return false;
-       else if(s1.get_action_score() < s2.get_action_score())
-	 return true;
-       else if(s2.get_action_score() < s1.get_action_score())
-	 return false;
+      // NB: the correctness of this code depends implicitly on the
+      // invariant that the score is a function of the contents of the
+      // solution.  Essentially we're using the score as a sort of
+      // hash value on solutions!
+      if(s1.get_score() < s2.get_score())
+	return true;
+      else if(s2.get_score() < s1.get_score())
+	return false;
+      else if(s1.get_action_score() < s2.get_action_score())
+	return true;
+      else if(s2.get_action_score() < s1.get_action_score())
+	return false;
 
-       const imm::map<package,action>
-	 &a1=s1.get_actions(), &a2=s2.get_actions();
-       const imm::set<dep>
-	 &us1=s1.get_unresolved_soft_deps(), &us2=s2.get_unresolved_soft_deps();
+      const imm::map<package,action>
+	&a1=s1.get_actions(), &a2=s2.get_actions();
+      const imm::set<dep>
+	&us1=s1.get_unresolved_soft_deps(), &us2=s2.get_unresolved_soft_deps();
 
 
-       // Speed hack: order by size first to avoid traversing the whole
-       // tree.
-       if(a1.size() < a2.size())
-	 return true;
-       else if(a2.size() < a1.size())
-	 return false;
-       else if(us1.size() < us2.size())
-	 return true;
-       else if(us2.size() < us1.size())
-	 return false;
-       else
-	 return a1 < a2 || (a2 == a1 && us1 < us2);
-     }
-   };
+      // Speed hack: order by size first to avoid traversing the whole
+      // tree.
+      if(a1.size() < a2.size())
+	return true;
+      else if(a2.size() < a1.size())
+	return false;
+      else if(us1.size() < us2.size())
+	return true;
+      else if(us2.size() < us1.size())
+	return false;
+      else
+	return a1 < a2 || (a2 == a1 && us1 < us2);
+    }
+  };
+
+  class instance_tracker;
+  friend class instance_tracker;
+  class instance_tracker
+  {
+    generic_problem_resolver &r;
+  public:
+    instance_tracker(generic_problem_resolver &_r)
+      :r(_r)
+    {
+      threads::mutex::lock l(r.execution_mutex);
+      if(r.solver_executing)
+	throw DoubleRunException();
+      else
+	r.solver_executing = true;
+    }
+
+    ~instance_tracker()
+    {
+      threads::mutex::lock l(r.execution_mutex);
+      assert(r.solver_executing);
+
+      r.solver_executing = false;
+      r.solver_cancelled = false;
+    }
+  };
 
    // Information regarding the weight given to various parameters;
    // packaged up in a struct so it can be easily used by the solution
@@ -304,6 +331,27 @@
     */
    bool remove_stupid:1;
 
+
+   // Multithreading support variables.
+   //
+   //  These variables ensure (as a sanity-check) that only one thread
+   //  executes the solver function at once, and allow the executing
+   //  instance to be cleanly terminated.  They are managed by the
+   //  instance_tracker class (see above).
+
+   /** If \b true, a thread is currently executing in the solver. */
+   bool solver_executing : 1;
+
+   /** If \b true, the currently executing thread should stop at the
+    *  next opportunity.
+    */
+   bool solver_cancelled : 1;
+
+   /** Mutex guarding the solver_executing and stop_solver. */
+   threads::mutex execution_mutex;
+
+
+
    /** The working queue: */
    std::priority_queue<solution, std::vector<solution>, solution_goodness_compare> open;
 
@@ -346,6 +394,7 @@
    /** Stores generated solutions: */
    std::vector<solution> generated_solutions;
 
+
    typedef std::set<std::pair<version, version> > stupid_table;
 
    std::ostream &dump_conflict(std::ostream &out, const imm::map<package, action> &conflict) const;
@@ -2071,6 +2120,16 @@
       unreject_version(*si);
   }
 
+  /** Cancel any find_next_solution call that is executing in the
+   *  background.
+   */
+  void cancel_solver()
+  {
+    threads::mutex::lock l(execution_mutex);
+    if(solver_executing)
+      solver_cancelled = true;
+  }
+
   /** Try to find the "next" solution: remove partial solutions from
    *  the open queue and place them in the closed queue until one of
    *  the following occurs:
@@ -2096,6 +2155,13 @@
    */
   solution find_next_solution(int max_steps)
   {
+    // This object is responsible for managing the instance variables
+    // that control threaded operation: it sets solver_executing when
+    // it is created and clears both solver_executing and
+    // solver_cancelled when it is destroyed.
+    instance_tracker t(*this);
+ 
+
     // Counter for debugging (see below)
     int odometer = 0;
 
@@ -2118,6 +2184,14 @@
 
     while(max_steps>0 && !open.empty())
       {
+	// Threaded operation: check whether we have been cancelled.
+	{
+	  threads::mutex::lock l(execution_mutex);
+	  if(solver_cancelled)
+	    throw InterruptedException();
+	}
+
+
 	solution s=open.top();
 	open.pop();
 



More information about the Aptitude-svn-commit mailing list