mirror of
				https://github.com/GayPizzaSpecifications/darwin-apk.git
				synced 2025-11-04 07:59:38 +00:00 
			
		
		
		
	WIP parallel sort (BROKEN)
This commit is contained in:
		@ -3,6 +3,8 @@
 | 
				
			|||||||
 * SPDX-License-Identifier: Apache-2.0
 | 
					 * SPDX-License-Identifier: Apache-2.0
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Foundation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class ApkPackageGraph {
 | 
					public class ApkPackageGraph {
 | 
				
			||||||
  public let pkgIndex: ApkIndex
 | 
					  public let pkgIndex: ApkIndex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -26,9 +28,9 @@ public class ApkPackageGraph {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for package in pkgIndex.packages {
 | 
					    for (id, package) in pkgIndex.packages.enumerated() {
 | 
				
			||||||
      self._nodes.append(.init(
 | 
					      self._nodes.append(.init(self,
 | 
				
			||||||
        package: package,
 | 
					        id: id,
 | 
				
			||||||
        children: package.dependencies.compactMap { dependency in
 | 
					        children: package.dependencies.compactMap { dependency in
 | 
				
			||||||
          guard let id = provides[dependency.requirement.name] else {
 | 
					          guard let id = provides[dependency.requirement.name] else {
 | 
				
			||||||
            return nil
 | 
					            return nil
 | 
				
			||||||
@ -62,3 +64,114 @@ public class ApkPackageGraph {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extension ApkPackageGraph {
 | 
				
			||||||
 | 
					  func findDependencyCycle(node: ApkPackageGraphNode) -> (ApkPackageGraphNode, ApkPackageGraphNode)? {
 | 
				
			||||||
 | 
					    var resolving = Set<ApkPackageGraphNode>()
 | 
				
			||||||
 | 
					    var visited = Set<ApkPackageGraphNode>()
 | 
				
			||||||
 | 
					    return self.findDependencyCycle(node: node, &resolving, &visited)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  func findDependencyCycle(
 | 
				
			||||||
 | 
					    node: ApkPackageGraphNode,
 | 
				
			||||||
 | 
					    _ resolving: inout Set<ApkPackageGraphNode>,
 | 
				
			||||||
 | 
					    _ visited: inout Set<ApkPackageGraphNode>
 | 
				
			||||||
 | 
					  ) -> (ApkPackageGraphNode, ApkPackageGraphNode)? {
 | 
				
			||||||
 | 
					    for dependency in node.children {
 | 
				
			||||||
 | 
					      let depNode = self._nodes[dependency.packageID]
 | 
				
			||||||
 | 
					      if resolving.contains(depNode) {
 | 
				
			||||||
 | 
					        return (node, depNode)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if !visited.contains(depNode) {
 | 
				
			||||||
 | 
					        resolving.insert(depNode)
 | 
				
			||||||
 | 
					        if let cycle = findDependencyCycle(node: depNode, &resolving, &visited) {
 | 
				
			||||||
 | 
					          return cycle
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        resolving.remove(depNode)
 | 
				
			||||||
 | 
					        visited.insert(depNode)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return nil
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public func parallelOrderSort(breakCycles: Bool = true) throws(SortError) -> [[ApkPackageGraphNode]] {
 | 
				
			||||||
 | 
					    var results = [[ApkPackageGraphNode]]()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Map all nodes to all of their children, remove any self dependencies
 | 
				
			||||||
 | 
					    var working = self._nodes.reduce(into: [ApkPackageGraphNode: Set<ApkPackageGraphNode>]()) { d, node in
 | 
				
			||||||
 | 
					      d[node] = Set(node.children.filter { child in
 | 
				
			||||||
 | 
					        if case .dep(let version) = child.constraint {
 | 
				
			||||||
 | 
					          version != .conflict && child.packageID != node.packageID
 | 
				
			||||||
 | 
					        } else { false }
 | 
				
			||||||
 | 
					      }.map { self._nodes[$0.packageID] })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Collect all child nodes that aren't already in the map
 | 
				
			||||||
 | 
					    // This should be empty every time
 | 
				
			||||||
 | 
					    let extras = working.values.reduce(Set<ApkPackageGraphNode>()) { a, b in
 | 
				
			||||||
 | 
					      a.union(b)
 | 
				
			||||||
 | 
					    }.subtracting(working.keys)
 | 
				
			||||||
 | 
					    assert(extras.isEmpty, "Dangling nodes in the graph")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Put all extra nodes into the working map, with an empty set
 | 
				
			||||||
 | 
					    extras.forEach {
 | 
				
			||||||
 | 
					      working[$0] = .init()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while true {
 | 
				
			||||||
 | 
					      // Set of all nodes now with satisfied dependencies
 | 
				
			||||||
 | 
					      var set = working
 | 
				
			||||||
 | 
					        .filter { _, children in children.isEmpty }
 | 
				
			||||||
 | 
					        .map(\.key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // If nothing was satisfied in this loop, check for cycles
 | 
				
			||||||
 | 
					      // If no cycles exist and the working set is empty, resolve is complete
 | 
				
			||||||
 | 
					      if set.isEmpty {
 | 
				
			||||||
 | 
					        if working.isEmpty {
 | 
				
			||||||
 | 
					          break
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let cycles = working.keys.compactMap { node in
 | 
				
			||||||
 | 
					          self.findDependencyCycle(node: node)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Error if cycle breaking is turned off
 | 
				
			||||||
 | 
					        if !breakCycles {
 | 
				
			||||||
 | 
					          throw .cyclicDependency(cycles: cycles.map { node, dependency in
 | 
				
			||||||
 | 
					              "\(node) -> \(dependency)"
 | 
				
			||||||
 | 
					            }.joined(separator: "\n"))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Bread cycles by setting the new resolution set to dependencies that cycled
 | 
				
			||||||
 | 
					        set = cycles.map(\.1)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Add installation set to list of installation sets
 | 
				
			||||||
 | 
					      results.append(set)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Filter the working set for anything that wasn't dealt with this iteration
 | 
				
			||||||
 | 
					      working = working.filter { node, _ in
 | 
				
			||||||
 | 
					        !set.contains(node)
 | 
				
			||||||
 | 
					      }.reduce(into: [ApkPackageGraphNode: Set<ApkPackageGraphNode>]()) { d, node in
 | 
				
			||||||
 | 
					        d[node.key] = node.value.subtracting(set)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print(working)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return results
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public enum SortError: Error, LocalizedError {
 | 
				
			||||||
 | 
					    case cyclicDependency(cycles: String)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var errorDescription: String {
 | 
				
			||||||
 | 
					      switch self {
 | 
				
			||||||
 | 
					      case .cyclicDependency(let cycles): "Dependency cycles found:\n\(cycles)"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,20 +6,35 @@
 | 
				
			|||||||
import Foundation
 | 
					import Foundation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class ApkPackageGraphNode {
 | 
					public class ApkPackageGraphNode {
 | 
				
			||||||
  private weak var graph: ApkPackageGraph!
 | 
					  private weak var _graph: ApkPackageGraph?
 | 
				
			||||||
  let package: ApkIndexPackage
 | 
					  let packageID: Int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  //private var _parents = NSHashTable<ApkPackageGraphNode>.weakObjects()
 | 
					  //private var _parents = NSHashTable<ApkPackageGraphNode>.weakObjects()
 | 
				
			||||||
  //private var _children = NSHashTable<ApkPackageGraphNode>.weakObjects()
 | 
					  //private var _children = NSHashTable<ApkPackageGraphNode>.weakObjects()
 | 
				
			||||||
  var parents = [ApkIndexRequirementRef]()
 | 
					  var parents = [ApkIndexRequirementRef]()
 | 
				
			||||||
  var children: [ApkIndexRequirementRef]
 | 
					  var children: [ApkIndexRequirementRef]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  internal init(package: ApkIndexPackage, children: [ApkIndexRequirementRef]) {
 | 
					  var package: ApkIndexPackage {
 | 
				
			||||||
    self.package = package
 | 
					    self._graph!.pkgIndex.packages[self.packageID]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  internal init(_ graph: ApkPackageGraph, id: Int, children: [ApkIndexRequirementRef]) {
 | 
				
			||||||
 | 
					    self._graph = graph
 | 
				
			||||||
 | 
					    self.packageID = id
 | 
				
			||||||
    self.children = children
 | 
					    self.children = children
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extension ApkPackageGraphNode: Equatable, Hashable {
 | 
				
			||||||
 | 
					  public static func == (lhs: ApkPackageGraphNode, rhs: ApkPackageGraphNode) -> Bool {
 | 
				
			||||||
 | 
					    lhs.packageID == rhs.packageID
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public func hash(into hasher: inout Hasher) {
 | 
				
			||||||
 | 
					    self.packageID.hash(into: &hasher)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extension ApkPackageGraphNode: CustomStringConvertible {
 | 
					extension ApkPackageGraphNode: CustomStringConvertible {
 | 
				
			||||||
  public var description: String {
 | 
					  public var description: String {
 | 
				
			||||||
    var result = "node[\(self.package.name)]"
 | 
					    var result = "node[\(self.package.name)]"
 | 
				
			||||||
 | 
				
			|||||||
@ -22,11 +22,20 @@ struct DpkGraphCommand: AsyncParsableCommand {
 | 
				
			|||||||
      fatalError(error.localizedDescription)
 | 
					      fatalError(error.localizedDescription)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if false
 | 
				
			||||||
    if var out = TextFileWriter(URL(filePath: "shallowIsolates.txt")) {
 | 
					    if var out = TextFileWriter(URL(filePath: "shallowIsolates.txt")) {
 | 
				
			||||||
      for node in graph.shallowIsolates { print(node, to: &out) }
 | 
					      for node in graph.shallowIsolates { print(node, to: &out) }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if var out = TextFileWriter(URL(filePath: "deepIsolates.txt")) {
 | 
					    if var out = TextFileWriter(URL(filePath: "deepIsolates.txt")) {
 | 
				
			||||||
      for node in graph.deepIsolates { print(node, to: &out) }
 | 
					      for node in graph.deepIsolates { print(node, to: &out) }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					    do {
 | 
				
			||||||
 | 
					      let sorted = try graph.parallelOrderSort()
 | 
				
			||||||
 | 
					      print(sorted)
 | 
				
			||||||
 | 
					    } catch {
 | 
				
			||||||
 | 
					      fatalError(error.localizedDescription)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user