diff --git a/build.sh b/build.sh index 1413134cc..ae7aa09bd 100755 --- a/build.sh +++ b/build.sh @@ -41,6 +41,7 @@ HAVE_CURL=0 HAVE_EXPAT=0 HAVE_GIT2=1 HAVE_LSTREAM=1 +HAVE_CPP20=0 RUBYINCLUDE="" RUBYINCLUDE2="" @@ -217,6 +218,9 @@ while [ "$*" != "" ]; do -nolstream) HAVE_LSTREAM=0 ;; + -cpp20) + HAVE_CPP20=1 + ;; -qt5) echo "*** WARNING: -qt5 option is ignored - Qt version is auto-detected now." ;; @@ -275,6 +279,7 @@ while [ "$*" != "" ]; do echo " -libpng Use libpng instead of Qt for PNG generation" echo " -nolibgit2 Do not include libgit2 for Git package support" echo " -nolstream Do not include the LStream plugin" + echo " -cpp20 Uses some C++20 features (e.g. atomics)" echo "" echo "Environment Variables:" echo "" @@ -670,6 +675,7 @@ qmake_options=( HAVE_PNG="$HAVE_PNG" HAVE_GIT2="$HAVE_GIT2" HAVE_LSTREAM="$HAVE_LSTREAM" + HAVE_CPP20="$HAVE_CPP20" PREFIX="$BIN" RPATH="$RPATH" KLAYOUT_VERSION="$KLAYOUT_VERSION" diff --git a/src/buddies/src/bd/bdWriterOptions.cc b/src/buddies/src/bd/bdWriterOptions.cc index dc8bc4287..8d9ccf24f 100644 --- a/src/buddies/src/bd/bdWriterOptions.cc +++ b/src/buddies/src/bd/bdWriterOptions.cc @@ -98,6 +98,20 @@ const std::string GenericWriterOptions::dxf_format_name = "DXF"; const std::string GenericWriterOptions::cif_format_name = "CIF"; const std::string GenericWriterOptions::mag_format_name = "MAG"; +std::vector +GenericWriterOptions::all_format_names () +{ + std::vector names; + names.push_back (gds2_format_name); + names.push_back (gds2text_format_name); + names.push_back (oasis_format_name); + names.push_back (lstream_format_name); + names.push_back (dxf_format_name); + names.push_back (cif_format_name); + names.push_back (mag_format_name); + return names; +} + void GenericWriterOptions::add_options (tl::CommandLineOptions &cmd, const std::string &format) { @@ -109,6 +123,15 @@ GenericWriterOptions::add_options (tl::CommandLineOptions &cmd, const std::strin "given factor." ); + if (format.empty ()) { + cmd << tl::arg (group + + "-of|--format=format", &m_format, "Specifies the output format", + "By default, the output format is derived from the file name suffix. " + "You can also specify the format directly using this option. Allowed format names are: " + + tl::join (all_format_names (), ", ") + ); + } + if (format.empty () || format == gds2_format_name || format == gds2text_format_name || format == oasis_format_name) { cmd << tl::arg (group + "-od|--dbu-out=dbu", &m_dbu, "Uses the specified database unit", @@ -426,6 +449,22 @@ GenericWriterOptions::configure (db::SaveLayoutOptions &save_options, const db:: save_options.set_keep_instances (m_keep_instances); save_options.set_write_context_info (m_write_context_info); + if (! m_format.empty ()) { + + // check, if the format name is a valid one + std::vector af = all_format_names (); + auto i = af.begin (); + while (i != af.end () && *i != m_format) { + ++i; + } + if (i == af.end ()) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid fornat name %s. Allowed names are: %s")), m_format, tl::join (af, ", "))); + } + + save_options.set_format (m_format); + + } + save_options.set_option_by_name ("gds2_max_vertex_count", m_gds2_max_vertex_count); save_options.set_option_by_name ("gds2_no_zero_length_paths", m_gds2_no_zero_length_paths); save_options.set_option_by_name ("gds2_multi_xy_records", m_gds2_multi_xy_records); diff --git a/src/buddies/src/bd/bdWriterOptions.h b/src/buddies/src/bd/bdWriterOptions.h index 67119d1d8..b0831044a 100644 --- a/src/buddies/src/bd/bdWriterOptions.h +++ b/src/buddies/src/bd/bdWriterOptions.h @@ -26,6 +26,7 @@ #include "bdCommon.h" #include +#include namespace tl { @@ -60,6 +61,11 @@ class BD_PUBLIC GenericWriterOptions */ GenericWriterOptions (const db::SaveLayoutOptions &options); + /** + * @brief Gets a list with all format names available + */ + static std::vector all_format_names (); + /** * @brief Adds the generic options to the command line parser object * The format string gives a hint about the target format. Certain options will be @@ -114,6 +120,7 @@ class BD_PUBLIC GenericWriterOptions static const std::string mag_format_name; private: + std::string m_format; double m_scale_factor; double m_dbu; bool m_dont_write_empty_cells; diff --git a/src/db/db/dbCell.cc b/src/db/db/dbCell.cc index 802614a2b..7200b30da 100644 --- a/src/db/db/dbCell.cc +++ b/src/db/db/dbCell.cc @@ -945,7 +945,7 @@ std::string Cell::get_display_name () const { tl_assert (layout () != 0); - if (is_ghost_cell () && empty ()) { + if (is_real_ghost_cell ()) { return std::string ("(") + layout ()->cell_name (cell_index ()) + std::string (")"); } else { return layout ()->cell_name (cell_index ()); @@ -959,6 +959,20 @@ Cell::set_name (const std::string &name) layout ()->rename_cell (cell_index (), name.c_str ()); } +void +Cell::set_ghost_cell (bool g) +{ + // NOTE: this change is not undo managed + if (m_ghost_cell != g) { + + m_ghost_cell = g; + tl_assert (layout () != 0); + // To trigger a redraw and cell tree rebuild + layout ()->cell_name_changed (); + + } +} + void Cell::check_locked () const { diff --git a/src/db/db/dbCell.h b/src/db/db/dbCell.h index 5bfe8b61c..c616a105d 100644 --- a/src/db/db/dbCell.h +++ b/src/db/db/dbCell.h @@ -926,15 +926,26 @@ class DB_PUBLIC Cell } /** - * @brief Sets the "ghost cell" flag + * @brief Gets a value indicating whether the cell is a "real" ghost cell * - * See "is_ghost_cell" for a description of this property. + * A ghost cell is a real ghost cell only if the ghost cell flag is set + * and the cell is empty. Only in that case for example the cell is written + * to GDS files as a ghost cell. + * + * Otherwise, the ghost cell flag is mostly ignored. */ - void set_ghost_cell (bool g) + bool is_real_ghost_cell () const { - m_ghost_cell = g; + return m_ghost_cell && empty (); } + /** + * @brief Sets the "ghost cell" flag + * + * See "is_ghost_cell" for a description of this property. + */ + void set_ghost_cell (bool g); + /** * @brief Gets a value indicating whether the cell is locked * diff --git a/src/db/db/dbCompoundOperation.cc b/src/db/db/dbCompoundOperation.cc index adaf30abc..dc9953582 100644 --- a/src/db/db/dbCompoundOperation.cc +++ b/src/db/db/dbCompoundOperation.cc @@ -859,7 +859,7 @@ namespace struct generic_result_adaptor { public: - generic_result_adaptor (std::vector > *results) + generic_result_adaptor (std::vector > *results) : mp_results (results) { m_intermediate.reserve (results->size ()); diff --git a/src/db/db/dbCompoundOperation.h b/src/db/db/dbCompoundOperation.h index 719e9e267..cfbd5d1b7 100644 --- a/src/db/db/dbCompoundOperation.h +++ b/src/db/db/dbCompoundOperation.h @@ -1758,7 +1758,7 @@ class DB_PUBLIC compound_local_operation * Creates a local operation which utilizes the operation tree. "node" is the root of the operation tree. * Ownership of the node is *not* transferred to the local operation. */ - compound_local_operation (CompoundRegionOperationNode *node) + compound_local_operation (CompoundRegionOperationNode *node) : mp_node (node) { } @@ -1802,7 +1802,7 @@ class DB_PUBLIC compound_local_operation_with_properties * Creates a local operation which utilizes the operation tree. "node" is the root of the operation tree. * Ownership of the node is *not* transferred to the local operation. */ - compound_local_operation_with_properties (CompoundRegionOperationNode *node, db::PropertyConstraint prop_constraint) + compound_local_operation_with_properties (CompoundRegionOperationNode *node, db::PropertyConstraint prop_constraint) : mp_node (node), m_prop_constraint (prop_constraint) { // .. nothing yet .. diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index be8f84f17..f187468fd 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1686,6 +1686,69 @@ connected_clusters::join_cluster_with (typename local_cluster::id_type id, } } +template +void +connected_clusters::join_clusters_with (typename local_cluster::id_type id, typename std::set::id_type>::const_iterator with_from, typename std::set::id_type>::const_iterator with_to) +{ + if (with_from != with_to && *with_from == id) { + ++with_from; + } + if (with_from == with_to) { + return; + } + + connections_type &target = m_connections [id]; + std::set target_set; + bool target_set_valid = false; + + for (auto w = with_from; w != with_to; ++w) { + + local_clusters::join_cluster_with (id, *w); + + // handle the connections by translating + + typename std::map::id_type, connections_type>::iterator tc = m_connections.find (*w); + if (tc != m_connections.end ()) { + + connections_type &to_join = tc->second; + + for (connections_type::const_iterator c = to_join.begin (); c != to_join.end (); ++c) { + m_rev_connections [*c] = id; + } + + if (target.empty ()) { + + target.swap (to_join); + + } else if (! to_join.empty ()) { + + // Join while removing duplicates + if (! target_set_valid) { + target_set.insert (target.begin (), target.end ()); + target_set_valid = true; + } + + for (auto j = to_join.begin (); j != to_join.end (); ++j) { + if (target_set.find (*j) == target_set.end ()) { + target.push_back (*j); + target_set.insert (*j); + } + } + + } + + m_connections.erase (tc); + + } + + if (m_connected_clusters.find (*w) != m_connected_clusters.end ()) { + m_connected_clusters.insert (id); + m_connected_clusters.erase (*w); + } + + } +} + template typename local_cluster::id_type connected_clusters::find_cluster_with_connection (const ClusterInstance &inst) const @@ -1995,8 +2058,9 @@ struct hc_receiver typename std::set::const_iterator c = sc->begin (); typename std::set::const_iterator cc = c; - for (++cc; cc != sc->end (); ++cc) { - mp_cell_clusters->join_cluster_with (*c, *cc); + ++cc; + if (cc != sc->end ()) { + mp_cell_clusters->join_clusters_with (*c, cc, sc->end ()); } } diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 8c8d588e7..e24605270 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -1274,6 +1274,13 @@ class DB_PUBLIC_TEMPLATE connected_clusters */ void join_cluster_with (typename local_cluster::id_type id, typename local_cluster::id_type with_id); + /** + * @brief Joins a cluster id with the a set of clusters given by an iterator interval with_to .. with_from + * + * This function is equivalent to calling "join_cluster_with" multiple times, but more efficient. + */ + void join_clusters_with (typename local_cluster::id_type id, typename std::set::id_type>::const_iterator with_from, typename std::set::id_type>::const_iterator with_to); + /** * @brief An iterator delivering all clusters (even the connectors) * diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index f13935fd9..a6af1f211 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -714,7 +714,6 @@ local_processor_result_computation_task::perform () { mp_cell_contexts->compute_results (*mp_contexts, mp_cell, mp_op, m_output_layers, mp_proc); - // erase the contexts we don't need any longer { tl::MutexLocker locker (& mp_contexts->lock ()); @@ -734,7 +733,10 @@ local_processor_result_computation_task::perform () } #endif - mp_contexts->context_map ().erase (mp_cell); + // release some memory + auto ctx = mp_contexts->context_map ().find (mp_cell); + tl_assert (ctx != mp_contexts->context_map ().end ()); + ctx->second.cleanup (); } } @@ -881,15 +883,6 @@ void local_processor::run (local_operation *op, unsigned compute_results (contexts, op, output_layers); } -template -void local_processor::push_results (db::Cell *cell, unsigned int output_layer, const std::unordered_set &result) const -{ - if (! result.empty ()) { - tl::MutexLocker locker (&cell->layout ()->lock ()); - cell->shapes (output_layer).insert (result.begin (), result.end ()); - } -} - template void local_processor::compute_contexts (local_processor_contexts &contexts, const local_operation *op, unsigned int subject_layer, const std::vector &intruder_layers) const { @@ -1248,7 +1241,7 @@ local_processor::compute_results (local_processor_contexts::iterator cpc = contexts.context_map ().find (&mp_subject_layout->cell (*bu)); if (cpc != contexts.context_map ().end ()) { cpc->second.compute_results (contexts, cpc->first, op, output_layers, this); - contexts.context_map ().erase (cpc); + cpc->second.cleanup (); // release some memory } } @@ -1261,6 +1254,24 @@ local_processor::compute_results (local_processor_contextslock ()); + for (auto c = contexts.begin (); c != contexts.end (); ++c) { + + db::Cell *cell = c->first; + auto r = c->second.result ().begin (); + auto rend = c->second.result ().end (); + + for (auto o = output_layers.begin (); r != rend && o != output_layers.end (); ++o, ++r) { + if (! r->empty ()) { + cell->shapes (*o).insert (r->begin (), r->end ()); + } + } + + } + } } namespace { diff --git a/src/db/db/dbHierProcessor.h b/src/db/db/dbHierProcessor.h index 44705178f..c52dd372e 100644 --- a/src/db/db/dbHierProcessor.h +++ b/src/db/db/dbHierProcessor.h @@ -244,9 +244,20 @@ class DB_PUBLIC local_processor_cell_contexts return m_contexts.end (); } + const std::vector > &result () const + { + return m_result; + } + + void cleanup () + { + m_contexts.clear (); + } + private: const db::Cell *mp_intruder_cell; std::unordered_map > m_contexts; + std::vector > m_result; }; template @@ -558,7 +569,6 @@ class DB_PUBLIC local_processor size_t get_progress () const; void compute_contexts (db::local_processor_contexts &contexts, db::local_processor_cell_context *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const typename local_processor_cell_contexts::context_key_type &intruders, db::Coord dist) const; void issue_compute_contexts (db::local_processor_contexts &contexts, db::local_processor_cell_context *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts::context_key_type &intruders, db::Coord dist) const; - void push_results (db::Cell *cell, unsigned int output_layer, const std::unordered_set &result) const; void compute_local_cell (const db::local_processor_contexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const local_operation *op, const typename local_processor_cell_contexts::context_key_type &intruders, std::vector > &result) const; bool subject_cell_is_breakout (db::cell_index_type ci) const diff --git a/src/db/db/dbHierProcessor2.cc b/src/db/db/dbHierProcessor2.cc index a7909bb9b..5f7be3cc0 100644 --- a/src/db/db/dbHierProcessor2.cc +++ b/src/db/db/dbHierProcessor2.cc @@ -446,7 +446,12 @@ local_processor_cell_contexts::compute_results (const local_processo proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, *c->first, res); } - if (common.empty ()) { + bool common_empty = true; + for (auto c = common.begin (); common_empty && c != common.end (); ++c) { + common_empty = c->empty (); + } + + if (common_empty) { CRONOLOGY_COMPUTE_BRACKET(event_propagate) for (std::vector::const_iterator o = output_layers.begin (); o != output_layers.end (); ++o) { @@ -519,9 +524,30 @@ local_processor_cell_contexts::compute_results (const local_processo } - for (std::vector::const_iterator o = output_layers.begin (); o != output_layers.end (); ++o) { - size_t oi = o - output_layers.begin (); - proc->push_results (cell, *o, common[oi]); + // store the results + + bool common_empty = true; + for (auto c = common.begin (); common_empty && c != common.end (); ++c) { + common_empty = c->empty (); + } + + if (! common_empty) { + if (m_result.empty ()) { + m_result.swap (common); + } else { + if (m_result.size () < common.size ()) { + m_result.resize (common.size ()); + } + auto t = m_result.begin (); + for (auto s = common.begin (); s != common.end (); ++s, ++t) { + if (t->empty ()) { + t->swap (*s); + } else { + t->insert (s->begin (), s->end ()); + s->clear (); + } + } + } } } diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index dacfcbd4f..40557f375 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -1417,7 +1417,7 @@ Layout::add_cell (const char *name) if (cm != m_cell_map.end ()) { const db::Cell &c= cell (cm->second); - if (c.is_ghost_cell () && c.empty ()) { + if (c.is_real_ghost_cell ()) { // ghost cells are available as new cells - the idea is to // treat them as non-existing. return cm->second; diff --git a/src/db/db/dbLayoutStateModel.h b/src/db/db/dbLayoutStateModel.h index d6f209241..3524f75c2 100644 --- a/src/db/db/dbLayoutStateModel.h +++ b/src/db/db/dbLayoutStateModel.h @@ -183,6 +183,14 @@ class DB_PUBLIC LayoutStateModel return m_busy; } + /** + * @brief Issue a "cell name changed event" + */ + void cell_name_changed () + { + cell_name_changed_event (); + } + protected: friend class PropertiesRepository; @@ -191,14 +199,6 @@ class DB_PUBLIC LayoutStateModel */ virtual void do_update () { } - /** - * @brief Issue a "prop id's changed event" - */ - void cell_name_changed () - { - cell_name_changed_event (); - } - /** * @brief Issue a "layer properties changed event" */ diff --git a/src/db/db/dbPropertiesFilter.h b/src/db/db/dbPropertiesFilter.h index b11a0ae46..fbcb9fd10 100644 --- a/src/db/db/dbPropertiesFilter.h +++ b/src/db/db/dbPropertiesFilter.h @@ -69,19 +69,19 @@ class polygon_properties_filter : public PolygonFilter, public PropertiesFilter { public: - polygon_properties_filter (const tl::Variant &name, const tl::GlobPattern &pattern, bool inverse) + polygon_properties_filter (const tl::Variant &name, const tl::GlobPattern &pattern, bool inverse) : PropertiesFilter (name, pattern, inverse) { // .. nothing yet .. } - polygon_properties_filter (const tl::Variant &name, const tl::Variant &value, bool inverse) + polygon_properties_filter(const tl::Variant &name, const tl::Variant &value, bool inverse) : PropertiesFilter (name, value, inverse) { // .. nothing yet .. } - polygon_properties_filter (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse) + polygon_properties_filter (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse) : PropertiesFilter (name, from, to, inverse) { // .. nothing yet .. @@ -103,19 +103,19 @@ class generic_properties_filter : public BasicFilter, public PropertiesFilter { public: - generic_properties_filter (const tl::Variant &name, const tl::GlobPattern &pattern, bool inverse) + generic_properties_filter (const tl::Variant &name, const tl::GlobPattern &pattern, bool inverse) : PropertiesFilter (name, pattern, inverse) { // .. nothing yet .. } - generic_properties_filter (const tl::Variant &name, const tl::Variant &value, bool inverse) + generic_properties_filter (const tl::Variant &name, const tl::Variant &value, bool inverse) : PropertiesFilter (name, value, inverse) { // .. nothing yet .. } - generic_properties_filter (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse) + generic_properties_filter (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse) : PropertiesFilter (name, from, to, inverse) { // .. nothing yet .. diff --git a/src/db/db/dbPropertiesRepository.cc b/src/db/db/dbPropertiesRepository.cc index f10dd7050..22129dd52 100644 --- a/src/db/db/dbPropertiesRepository.cc +++ b/src/db/db/dbPropertiesRepository.cc @@ -27,6 +27,7 @@ #include "tlString.h" #include "tlAssert.h" #include "tlHash.h" +#include "tlStaticObjects.h" namespace db { @@ -348,13 +349,21 @@ PropertiesSet::hash () const // ---------------------------------------------------------------------------------- // PropertiesRepository implementation -static PropertiesRepository s_instance; +static PropertiesRepository *sp_global_instance = 0; static PropertiesRepository *sp_temp_instance = 0; PropertiesRepository & PropertiesRepository::instance () { - return sp_temp_instance ? *sp_temp_instance : s_instance; + if (sp_temp_instance) { + return *sp_temp_instance; + } else { + if (! sp_global_instance) { + sp_global_instance = new PropertiesRepository (); + tl::StaticObjects::reg (&sp_global_instance); + } + return *sp_global_instance; + } } void diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc index 6d0a20d60..186ed30db 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -89,7 +89,7 @@ struct simple_result_inserter { typedef TR value_type; - simple_result_inserter (std::unordered_set &result) + simple_result_inserter (std::unordered_set &result) : mp_result (&result) { // .. nothing yet .. @@ -232,6 +232,8 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel bool take_all = edge_check.has_negative_edge_output () || intruders.empty (); db::Box common_box; + bool subjects_are_fully_covered = false; + if (! take_all) { db::Vector e (edge_check.distance (), edge_check.distance ()); @@ -241,14 +243,24 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel subject_box += db::box_convert () (**i); } - if (edge_check.requires_different_layers ()) { - db::Box intruder_box; + common_box = subject_box.enlarged (e); + + if (edge_check.requires_different_layers () && ! common_box.empty ()) { + + subjects_are_fully_covered = false; + + db::Box all_intruders_box; + for (auto i = intruders.begin (); i != intruders.end (); ++i) { - intruder_box += db::box_convert () (**i); + db::Box intruder_box = db::box_convert () (**i); + if (! subjects_are_fully_covered && (*i)->is_box () && common_box.inside (intruder_box)) { + subjects_are_fully_covered = true; + } + all_intruders_box += intruder_box.enlarged (e); } - common_box = subject_box.enlarged (e) & intruder_box.enlarged (e); - } else { - common_box = subject_box.enlarged (e); + + common_box &= all_intruders_box; + } } @@ -306,6 +318,22 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel // empty intruders + } else if (subjects_are_fully_covered) { + + // optimization: can use a single box for the intruders + + n = 1; + + db::Point ul = common_box.upper_left (); + db::Point lr = common_box.lower_right (); + + poly_check.enter (db::Edge (common_box.p1 (), ul), n); + poly_check.enter (db::Edge (ul, common_box.p2 ()), n); + poly_check.enter (db::Edge (common_box.p2 (), lr), n); + poly_check.enter (db::Edge (lr, common_box.p1 ()), n); + + n += 2; + } else if (! m_other_is_merged && (intruders.size () > 1 || ! (*intruders.begin ())->is_box ())) { // NOTE: this local merge is not necessarily giving the same results than a global merge before running diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index b9218f533..4a53b12df 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -3450,6 +3450,9 @@ Class decl_Cell ("db", "Cell", "To satisfy the references inside the layout, a dummy cell is created in this case\n" "which has the \"ghost cell\" flag set to true.\n" "\n" + "A ghost cell is a real ghost cell only if the cell is empty. In that case, it is written " + "as a ghost cell to GDS files for example. If a cell is not empty, this flag is ignored.\n" + "\n" "This method has been introduced in version 0.20.\n" ) + gsi::method ("ghost_cell=", &db::Cell::set_ghost_cell, gsi::arg ("flag"), diff --git a/src/db/unit_tests/dbPropertiesRepositoryTests.cc b/src/db/unit_tests/dbPropertiesRepositoryTests.cc index 7363be12d..fe8dc27a3 100644 --- a/src/db/unit_tests/dbPropertiesRepositoryTests.cc +++ b/src/db/unit_tests/dbPropertiesRepositoryTests.cc @@ -503,3 +503,19 @@ TEST(SameValueDifferentTypes) EXPECT_EQ (db::property_name (rp.prop_name_id ((int) 5)).to_parsable_string (), "#5"); } } + +TEST(ComplexTypes) +{ + // This is also a smoke test: we intentionally register globally as the finalization code + // is critical: without the right destruction order we destroy the class object before the + // variant and trigger an assertion (pure virtual function called) + + db::PropertiesSet ps; + ps.insert (tl::Variant (17), db::DBox (0, 0, 1.5, 2.5)); + + db::properties_id_type pid = db::properties_id (ps); + + auto const &ps_out = db::properties (pid); + + EXPECT_EQ (ps_out.to_dict_var ().to_string (), "{17=>(0,0;1.5,2.5)}"); +} diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 627a64cc2..e2ff2b7ee 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -942,15 +942,18 @@ def capacitor_with_bulk(name, area_cap, cls = nil) # Turns profiling on or off (default). In profiling mode, the # system will collect statistics about rules executed, their execution time # and memory information. The argument specifies how many operations to - # print at the end of the run. Without an argument, all operations are + # print at the end of the run. Without an argument or when passing "true", all operations are # printed. Passing "false" for the argument will disable profiling. This is the # default. def profile(n = 0) - if !n.is_a?(1.class) && n != nil && n != false - raise("Argument to 'profile' must be either an integer number or nil") + if !n.is_a?(1.class) && n != nil && n != false && n != true + raise("Argument to 'profile' must be either an integer number, true, false or nil") end @profile = !!n + if n == true + n = 0 + end @profile_n = [n || 0, 0].max end diff --git a/src/edt/edt/RoundCornerOptionsDialog.ui b/src/edt/edt/RoundCornerOptionsDialog.ui index 7cfd7c675..586107f2e 100644 --- a/src/edt/edt/RoundCornerOptionsDialog.ui +++ b/src/edt/edt/RoundCornerOptionsDialog.ui @@ -11,7 +11,7 @@ - Dialog + Round Corners diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index ccb9abdbc..917122572 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -1770,6 +1770,7 @@ PartialService::edit_cancel () ui ()->ungrab_mouse (this); + clear_mouse_cursors (); close_editor_hooks (false); selection_to_view (); diff --git a/src/gsi/gsi/gsiIterators.h b/src/gsi/gsi/gsiIterators.h index 374102e8d..030d02686 100644 --- a/src/gsi/gsi/gsiIterators.h +++ b/src/gsi/gsi/gsiIterators.h @@ -40,7 +40,7 @@ template struct address_of; template struct address_of { - address_of () : b () { } + address_of () : b () { } const void *operator() (const X &x) const { b = x; return &b; } mutable X b; }; diff --git a/src/klayout.pri b/src/klayout.pri index 067942aec..c21ae29d8 100644 --- a/src/klayout.pri +++ b/src/klayout.pri @@ -204,12 +204,16 @@ msvc { # -Wno-reserved-user-defined-literal \ # - lessThan(QT_MAJOR_VERSION, 6) { - # because we use unordered_map/unordered_set: - QMAKE_CXXFLAGS += -std=c++11 + equals(HAVE_CPP20, "1") { + QMAKE_CXXFLAGS += -std=c++20 + DEFINES += HAVE_CPP20 } else { - # because we use unordered_map/unordered_set: - QMAKE_CXXFLAGS += -std=c++17 + lessThan(QT_MAJOR_VERSION, 6) { + # because we use unordered_map/unordered_set: + QMAKE_CXXFLAGS += -std=c++11 + } else { + QMAKE_CXXFLAGS += -std=c++17 + } } win32 { diff --git a/src/lay/lay/FillDialog.ui b/src/lay/lay/FillDialog.ui index 592435ffb..c78340400 100644 --- a/src/lay/lay/FillDialog.ui +++ b/src/lay/lay/FillDialog.ui @@ -86,87 +86,6 @@ - - - - - 0 - 0 - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 6 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Select how the region to fill is specified. - - - - All (whole cell) - - - - - Shapes on layer ... - - - - - Selected shapes - - - - - Single box with ... - - - - - Ruler bounding boxes - - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 20 - - - - - - - @@ -277,22 +196,6 @@ 6 - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 241 - 20 - - - - @@ -316,6 +219,22 @@ + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 241 + 20 + + + + @@ -436,6 +355,87 @@ + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Select how the region to fill is specified. + + + + All (whole cell) + + + + + Shapes on layer ... + + + + + Selected shapes + + + + + Single box with ... + + + + + Ruler bounding boxes + + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 20 + + + + + + + @@ -491,6 +491,9 @@ The fill will not be generated over the areas specified by these layers + + 3 + All layers @@ -810,6 +813,83 @@ + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Raster origin + + + + + + + + 0 + 0 + + + + + + + + (x, y of lower-left corner of fill cell in µm) + + + + + + + Qt::Horizontal + + + + 135 + 20 + + + + + + + @@ -837,13 +917,6 @@ 6 - - - - µm (keep distance between fill cells unless stitched) - - - @@ -851,39 +924,13 @@ - - - - - 0 - 0 - - - - Leave empty for no distance. Otherwise enter a distance in micron (can be anisotropic in the form "dx,dy") - - - - - + + - ... + µm (keep distance between fill cells unless stitched) - - - - Qt::Horizontal - - - - 141 - 20 - - - - @@ -891,6 +938,13 @@ + + + + ... + + + @@ -904,16 +958,6 @@ - - - - The second order fill cell is used to fill space remaining from the first fill step. Thus, the second order fill cell must be smaller than the first order fill cell. The boundary layer must be the same for the second order fill cell. - - - true - - - @@ -980,6 +1024,42 @@ + + + + Qt::Horizontal + + + + 141 + 20 + + + + + + + + The second order fill cell is used to fill space remaining from the first fill step. Thus, the second order fill cell must be smaller than the first order fill cell. The boundary layer must be the same for the second order fill cell. + + + true + + + + + + + + 0 + 0 + + + + Leave empty for no distance. Otherwise enter a distance in micron (can be anisotropic in the form "dx,dy") + + + @@ -1030,6 +1110,7 @@ row_le column_le enhanced_cb + origin_le second_order_fill_cb fill_cell_2nd_le choose_fc_2nd_pb diff --git a/src/lay/lay/layFillDialog.cc b/src/lay/lay/layFillDialog.cc index 6ed037fc8..1d02e8241 100644 --- a/src/lay/lay/layFillDialog.cc +++ b/src/lay/lay/layFillDialog.cc @@ -92,6 +92,7 @@ FillDialog::FillDialog (QWidget *parent, LayoutViewBase *view) fill_area_stack->setCurrentIndex (0); connect (fill_area_cbx, SIGNAL (currentIndexChanged (int)), this, SLOT (fill_area_changed (int))); + connect (enhanced_cb, SIGNAL (stateChanged (int)), this, SLOT (enhanced_fill_changed (int))); connect (button_box, SIGNAL (accepted ()), this, SLOT (ok_pressed ())); connect (choose_fc_pb, SIGNAL (clicked ()), this, SLOT (choose_fc ())); connect (choose_fc_2nd_pb, SIGNAL (clicked ()), this, SLOT (choose_fc_2nd ())); @@ -167,6 +168,8 @@ FillDialog::generate_fill (const FillParameters &fp) bool enhanced_fill = enhanced_cb->isChecked (); + db::Point fill_origin = db::CplxTrans (ly.dbu ()).inverted () * fp.fill_origin; + db::Coord exclude_x = db::coord_traits::rounded (fp.exclude_distance.x () / ly.dbu ()); db::Coord exclude_y = db::coord_traits::rounded (fp.exclude_distance.y () / ly.dbu ()); @@ -232,8 +235,6 @@ FillDialog::generate_fill (const FillParameters &fp) fill_region.merge (); } - db::Box fr_bbox = fill_region.bbox (); - if (tl::verbosity () >= 20) { tl::info << "Collecting exclude areas"; } @@ -263,8 +264,6 @@ FillDialog::generate_fill (const FillParameters &fp) // Perform the NOT operation to create the fill region fill_region -= es; - db::Region new_fill_area; - int step = 0; do { @@ -276,7 +275,7 @@ FillDialog::generate_fill (const FillParameters &fp) } if (! enhanced_fill) { - db::fill_region (cv.cell (), fill_region, fill_cell->cell_index (), fc_bbox, row_step, column_step, fr_bbox.p1 (), false, fill_cell2 ? &fill_region : 0, fill_margin, fill_cell2 ? &fill_region : 0); + db::fill_region (cv.cell (), fill_region, fill_cell->cell_index (), fc_bbox, row_step, column_step, fill_origin, false, fill_cell2 ? &fill_region : 0, fill_margin, fill_cell2 ? &fill_region : 0); } else { db::fill_region_repeat (cv.cell (), fill_region, fill_cell->cell_index (), fc_bbox, row_step, column_step, fill_margin, fill_cell2 ? &fill_region : 0); } @@ -478,6 +477,20 @@ FillDialog::get_fill_parameters () fp.enhanced_fill = enhanced_cb->isChecked (); + // read origin + x = 0.0, y = 0.0; + s = tl::to_string (origin_le->text ()); + ex = tl::Extractor (s.c_str ()); + if (ex.try_read (x)) { + if (ex.test (",") && ex.try_read (y)) { + // take x, y + } else { + y = x; + } + } + + fp.fill_origin = db::DPoint (x, y); + db::DBox fc_bbox = db::CplxTrans (cv->layout ().dbu ()) * (fc_bbox_layer < 0 ? fill_cell->bbox () : fill_cell->bbox (fc_bbox_layer)); if (fc_bbox.empty ()) { if (fc_bbox_layer >= 0) { @@ -492,7 +505,7 @@ FillDialog::get_fill_parameters () if (ex.try_read (x) && ex.test (",") && ex.try_read (y)) { fp.row_step = db::DVector (x, y); } else { - fp.row_step = db::DVector (fc_bbox.width (), 0.0); + fp.row_step = db::DVector (fc_bbox.width () + fp.fill_cell_margin.x (), 0.0); } s = tl::to_string (column_le->text ()); @@ -500,7 +513,7 @@ FillDialog::get_fill_parameters () if (ex.try_read (x) && ex.test (",") && ex.try_read (y)) { fp.column_step = db::DVector (x, y); } else { - fp.column_step = db::DVector (0.0, fc_bbox.height ()); + fp.column_step = db::DVector (0.0, fc_bbox.height () + fp.fill_cell_margin.y ()); } fp.fc_bbox = fc_bbox; @@ -528,7 +541,7 @@ FillDialog::get_fill_parameters () if (ex.try_read (x) && ex.test (",") && ex.try_read (y)) { fp.row_step2 = db::DVector (x, y); } else { - fp.row_step2 = db::DVector (fc_bbox2.width (), 0.0); + fp.row_step2 = db::DVector (fc_bbox2.width () + fp.fill_cell_margin2.x (), 0.0); } s = tl::to_string (column_2nd_le->text ()); @@ -536,7 +549,7 @@ FillDialog::get_fill_parameters () if (ex.try_read (x) && ex.test (",") && ex.try_read (y)) { fp.column_step2 = db::DVector (x, y); } else { - fp.column_step2 = db::DVector (0.0, fc_bbox2.height ()); + fp.column_step2 = db::DVector (0.0, fc_bbox2.height () + fp.fill_cell_margin2.y ()); } fp.fc_bbox2 = fc_bbox2; @@ -575,6 +588,12 @@ BEGIN_PROTECTED END_PROTECTED } +void +FillDialog::enhanced_fill_changed (int ef) +{ + origin_le->setEnabled (ef != Qt::Checked); +} + void FillDialog::fill_area_changed (int fa) { diff --git a/src/lay/lay/layFillDialog.h b/src/lay/lay/layFillDialog.h index cbd614e0f..bb4706cd5 100644 --- a/src/lay/lay/layFillDialog.h +++ b/src/lay/lay/layFillDialog.h @@ -53,6 +53,7 @@ struct LAY_PUBLIC FillParameters bool exclude_all_layers; std::vector exclude_layers; FillRegionMode fill_region_mode; + db::DPoint fill_origin; db::Region fill_region; db::LayerProperties fill_region_layer; db::DVector exclude_distance; @@ -81,6 +82,7 @@ Q_OBJECT public slots: void fill_area_changed (int); + void enhanced_fill_changed (int); void ok_pressed (); void choose_fc (); void choose_fc_2nd (); diff --git a/src/laybasic/laybasic/layFinder.cc b/src/laybasic/laybasic/layFinder.cc index 81c969f41..ef7c7c97b 100644 --- a/src/laybasic/laybasic/layFinder.cc +++ b/src/laybasic/laybasic/layFinder.cc @@ -220,7 +220,7 @@ Finder::do_find (const db::Cell &cell, int level, const db::DCplxTrans &vp, cons && (t * m_cell_box_convert (cell)).touches (m_scan_region) && (mp_view->select_inside_pcells_mode () || !cell.is_proxy ()) && !mp_view->is_cell_hidden (cell.cell_index (), m_cv_index) - && !cell.is_ghost_cell ()) { + && !cell.is_real_ghost_cell ()) { db::ICplxTrans it = t.inverted (); db::Box scan_box (it * m_scan_region); @@ -846,7 +846,7 @@ InstFinder::reset_counter () bool InstFinder::consider_cell (const db::Cell &cell) const { - return cell.is_ghost_cell () ? m_consider_ghost_cells : m_consider_normal_cells; + return cell.is_real_ghost_cell () ? m_consider_ghost_cells : m_consider_normal_cells; } void @@ -877,7 +877,7 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d // just consider the instances exactly at the last level of // hierarchy (this is where the boxes are drawn) or of cells that // are hidden. - if (level == max_level () - 1 || inst_cell.is_proxy () || inst_cell.is_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) { + if (level == max_level () - 1 || inst_cell.is_proxy () || inst_cell.is_real_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) { db::box_convert bc (layout ()); for (db::CellInstArray::iterator p = cell_inst.begin_touching (search_box, bc); ! p.at_end (); ++p) { @@ -885,7 +885,7 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d checkpoint (); db::Box ibox; - if (! m_visible_layers || level == mp_view->get_max_hier_levels () - 1 || inst_cell.is_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) { + if (! m_visible_layers || level == mp_view->get_max_hier_levels () - 1 || inst_cell.is_real_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) { ibox = inst_cell.bbox_with_empty (); } else if (inst_cell.bbox ().empty ()) { // empty cells cannot be found by visible layers, so we always select them here @@ -954,7 +954,7 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d // just consider the instances exactly at the last level of // hierarchy (this is where the boxes are drawn) or if of cells that // are hidden. - if (level == max_level () - 1 || inst_cell.is_proxy () || inst_cell.is_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) { + if (level == max_level () - 1 || inst_cell.is_proxy () || inst_cell.is_real_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) { db::box_convert bc (layout ()); for (db::CellInstArray::iterator p = cell_inst.begin_touching (search_box, bc); ! p.at_end (); ++p) { @@ -965,7 +965,7 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d double d = std::numeric_limits::max (); db::Box ibox; - if (! m_visible_layers || level == mp_view->get_max_hier_levels () - 1 || inst_cell.is_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) { + if (! m_visible_layers || level == mp_view->get_max_hier_levels () - 1 || inst_cell.is_real_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) { ibox = inst_cell.bbox_with_empty (); } else if (inst_cell.bbox ().empty ()) { // empty cells cannot be found by visible layers, so we always select them here diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc index 742af551a..a93639741 100644 --- a/src/laybasic/laybasic/layMove.cc +++ b/src/laybasic/laybasic/layMove.cc @@ -269,9 +269,12 @@ MoveService::mouse_double_click_event (const db::DPoint &p, unsigned int buttons handle_click (p, buttons, false, 0); } - lay::SelectionService *selector = mp_view->selection_service (); - if (selector) { - return selector->mouse_double_click_event (p, buttons, prio); + if (is_active ()) { + // in move mode, a double click opens the properties dialog + lay::SelectionService *selector = mp_view->selection_service (); + if (selector) { + return selector->mouse_double_click_event (p, buttons, prio); + } } } diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.cc b/src/laybasic/laybasic/layRedrawThreadWorker.cc index d05a4de63..f193fa1cd 100644 --- a/src/laybasic/laybasic/layRedrawThreadWorker.cc +++ b/src/laybasic/laybasic/layRedrawThreadWorker.cc @@ -714,7 +714,7 @@ RedrawThreadWorker::setup (LayoutViewBase *view, RedrawThreadCanvas *canvas, con std::set &gc = m_ghost_cells [i]; const db::Layout &ly = view->cellview (i)->layout (); for (auto c = ly.begin (); c != ly.end (); ++c) { - if (c->is_ghost_cell ()) { + if (c->is_real_ghost_cell ()) { gc.insert (c->cell_index ()); } } @@ -950,12 +950,12 @@ RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type c bbox_for_label = bbox; } - if (for_ghosts && cell.is_ghost_cell ()) { + if (for_ghosts && cell.is_real_ghost_cell ()) { // paint the box on this level draw_cell (drawing_context, level, trans, bbox, bbox_for_label, empty_cell, mp_layout->display_name (ci), opt_bitmap); - } else if (! for_ghosts && ! cell.is_ghost_cell () && (level == m_to_level || (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ()))) { + } else if (! for_ghosts && ! cell.is_real_ghost_cell () && (level == m_to_level || (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ()))) { // paint the box on this level draw_cell (drawing_context, level, trans, bbox, bbox_for_label, empty_cell, mp_layout->display_name (ci), opt_bitmap); @@ -1186,12 +1186,12 @@ RedrawThreadWorker::draw_box_properties_impl (bool drawing_context, db::cell_ind // small cell dropped - } else if (for_ghosts && cell.is_ghost_cell ()) { + } else if (for_ghosts && cell.is_real_ghost_cell ()) { // paint the box on this level draw_cell_properties (drawing_context, level, trans, bbox, prop_id); - } else if (! for_ghosts && ! cell.is_ghost_cell () && (level == m_to_level || (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ()))) { + } else if (! for_ghosts && ! cell.is_real_ghost_cell () && (level == m_to_level || (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ()))) { // paint the box on this level draw_cell_properties (drawing_context, level, trans, bbox, prop_id); @@ -1317,7 +1317,7 @@ RedrawThreadWorker::any_shapes (db::cell_index_type cell_index, unsigned int lev // Ghost cells are not drawn either const db::Cell &cell = mp_layout->cell (cell_index); - if (cell.is_ghost_cell ()) { + if (cell.is_real_ghost_cell ()) { return false; } @@ -1359,7 +1359,7 @@ RedrawThreadWorker::any_cell_box (db::cell_index_type cell_index, unsigned int l // ghost cells are also drawn const db::Cell &cell = mp_layout->cell (cell_index); - if (cell.is_ghost_cell ()) { + if (cell.is_real_ghost_cell ()) { return true; } @@ -1400,7 +1400,7 @@ RedrawThreadWorker::any_text_shapes (db::cell_index_type cell_index, unsigned in // Ghost cells are not drawn either const db::Cell &cell = mp_layout->cell (cell_index); - if (cell.is_ghost_cell ()) { + if (cell.is_real_ghost_cell ()) { return false; } @@ -1503,7 +1503,7 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c } else if (! bbox.empty ()) { - bool hidden = cell.is_ghost_cell () || ((m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ())); + bool hidden = cell.is_real_ghost_cell () || ((m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ())); bool need_to_dive = (level + 1 < m_to_level) && ! hidden; db::Box cell_bbox = cell.bbox (); @@ -1637,7 +1637,7 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c ++inst; db::cell_index_type new_ci = cell_inst.object ().cell_index (); - bool hidden = mp_layout->cell (new_ci).is_ghost_cell () || ((m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (new_ci) != m_hidden_cells [m_cv_index].end ())); + bool hidden = mp_layout->cell (new_ci).is_real_ghost_cell () || ((m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (new_ci) != m_hidden_cells [m_cv_index].end ())); db::Box cell_box = mp_layout->cell (new_ci).bbox (m_layer); if (! cell_box.empty () && ! hidden) { @@ -1939,7 +1939,7 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_ ++inst; db::cell_index_type new_ci = cell_inst.object ().cell_index (); - bool hidden = mp_layout->cell (new_ci).is_ghost_cell () || ((m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (new_ci) != m_hidden_cells [m_cv_index].end ())); + bool hidden = mp_layout->cell (new_ci).is_real_ghost_cell () || ((m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (new_ci) != m_hidden_cells [m_cv_index].end ())); db::Box new_cell_box = mp_layout->cell (new_ci).bbox (m_layer); if (! new_cell_box.empty () && ! hidden) { @@ -2096,7 +2096,7 @@ RedrawThreadWorker::draw_layer (int from_level, int to_level, db::cell_index_typ db::Box cell_bbox = cell.bbox (); // Nothing to draw - if (bbox.empty () || cell.is_ghost_cell ()) { + if (bbox.empty () || cell.is_real_ghost_cell ()) { return; } diff --git a/src/laybasic/unit_tests/layAnnotationShapes.cc b/src/laybasic/unit_tests/layAnnotationShapes.cc index 98a142939..f6a8cd56a 100644 --- a/src/laybasic/unit_tests/layAnnotationShapes.cc +++ b/src/laybasic/unit_tests/layAnnotationShapes.cc @@ -34,7 +34,7 @@ class shape_as_user_object : public db::DUserObjectBase { public: - shape_as_user_object (const Sh &sh) + shape_as_user_object (const Sh &sh) : m_shape (sh) { } diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc index 3f37a0606..c6b7a3ea2 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc @@ -577,7 +577,7 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S // don't write ghost cells unless they are not empty (any more) // also don't write proxy cells which are not employed - if ((! cref.is_ghost_cell () || ! cref.empty ()) && (! cref.is_proxy () || ! cref.is_top ())) { + if (! cref.is_real_ghost_cell () && (! cref.is_proxy () || ! cref.is_top ())) { try { write_cell (layout, cref, layers, cell_set, sf, time_data); diff --git a/src/plugins/streamers/lstream/db_plugin/lstrWriter.cc b/src/plugins/streamers/lstream/db_plugin/lstrWriter.cc index 4de9a2fc4..ccd31f815 100644 --- a/src/plugins/streamers/lstream/db_plugin/lstrWriter.cc +++ b/src/plugins/streamers/lstream/db_plugin/lstrWriter.cc @@ -913,7 +913,7 @@ Writer::make_meta_data (const db::Cell *cell, stream::metaData::MetaData::Builde void Writer::write_cell (db::cell_index_type ci, kj::BufferedOutputStream &os) { - bool needs_layout_view = ! mp_layout->cell (ci).is_ghost_cell (); + bool needs_layout_view = ! mp_layout->cell (ci).is_real_ghost_cell (); bool needs_meta_data_view = mp_layout->begin_meta (ci) != mp_layout->end_meta (ci); capnp::MallocMessageBuilder message; diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index 2b3256808..124df7e44 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -1322,21 +1322,37 @@ OASISReader::resolve_forward_references (db::PropertiesSet &properties) const tl::Variant &name = db::property_name (p->first); if (name.is_id ()) { - std::map ::iterator pf = m_propname_forward_references.find (name.to_id ()); + uint64_t name_id = name.to_id (); + bool is_sprop = (name_id & 1) != 0; + name_id >>= 1; + + std::map ::iterator pf = m_propname_forward_references.find (name_id); if (pf != m_propname_forward_references.end ()) { - if (pf->second == m_s_gds_property_name_id) { + if (pf->second == m_klayout_context_property_name_id) { + + // NOTE: property names ID 0 is reserved for context strings + new_props.insert (property_names_id_type (0), value); + + } else if (! m_read_properties) { + + // ignore other properties + + } else if (pf->second == m_s_gds_property_name_id && is_sprop) { // S_GDS_PROPERTY translation if (value.is_list () && value.get_list ().size () >= 2) { new_props.insert (value.get_list () [0], value.get_list () [1]); } - } else if (pf->second == m_klayout_context_property_name_id) { - // NOTE: property names ID 0 is reserved for context strings - new_props.insert (property_names_id_type (0), value); + } else if (is_sprop && ! m_read_all_properties) { + + // ignore system properties + } else { + new_props.insert (pf->second, value); + } } @@ -1395,10 +1411,27 @@ OASISReader::replace_forward_references_in_variant (tl::Variant &v) } } +static void +store_properties (db::PropertiesSet &properties, db::property_names_id_type name, const db::OASISReader::property_value_list &value) +{ + if (value.size () == 0) { + properties.insert (name, tl::Variant ()); + } else if (value.size () == 1) { + properties.insert (name, tl::Variant (value [0])); + } else if (value.size () > 1) { + properties.insert (name, tl::Variant (value.begin (), value.end ())); + } +} + void OASISReader::store_last_properties (db::PropertiesSet &properties, bool ignore_special, bool with_context_props) { - if (with_context_props && mm_last_property_name.get () == m_klayout_context_property_name_id) { + const tl::Variant &name = db::property_name (mm_last_property_name.get ()); + if (name.is_id ()) { + + store_properties (properties, mm_last_property_name.get (), mm_last_value_list.get ()); + + } else if (with_context_props && mm_last_property_name.get () == m_klayout_context_property_name_id) { // Context properties are stored with a special property name ID of 0 @@ -1422,12 +1455,10 @@ OASISReader::store_last_properties (db::PropertiesSet &properties, bool ignore_s // This is mode is used for cells and layouts so the standard properties do not appear as user properties. // For shapes we need to keep the special ones since they may be forward-references S_GDS_PROPERTY names. - } else if (mm_last_value_list.get ().size () == 0) { - properties.insert (mm_last_property_name.get (), tl::Variant ()); - } else if (mm_last_value_list.get ().size () == 1) { - properties.insert (mm_last_property_name.get (), tl::Variant (mm_last_value_list.get () [0])); - } else if (mm_last_value_list.get ().size () > 1) { - properties.insert (mm_last_property_name.get (), tl::Variant (mm_last_value_list.get ().begin (), mm_last_value_list.get ().end ())); + } else { + + store_properties (properties, mm_last_property_name.get (), mm_last_value_list.get ()); + } } @@ -1510,7 +1541,7 @@ OASISReader::read_properties () std::map ::const_iterator cid = m_propnames.find (id); if (cid == m_propnames.end ()) { - mm_last_property_name = db::property_names_id (tl::Variant (id, true /*dummy for id type*/)); + mm_last_property_name = db::property_names_id (tl::Variant ((id << 1) + uint64_t (is_sprop ? 1 : 0), true /*dummy for id type*/)); m_propname_forward_references.insert (std::make_pair (id, db::property_names_id_type (0))); } else { mm_last_property_name = db::property_names_id (tl::Variant (cid->second)); diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index e7ce59048..49f7b7077 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -1443,12 +1443,6 @@ static bool must_write_cell (const db::Cell &cref) return ! cref.is_proxy () || ! cref.is_top (); } -static bool skip_cell_body (const db::Cell &cref) -{ - // Skip cell bodies for ghost cells unless empty (they are not longer ghost cells in this case) - return cref.is_ghost_cell () && cref.empty (); -} - void OASISWriter::create_cell_nstrings (const db::Layout &layout, const std::set &cell_set) { @@ -1670,7 +1664,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save mp_cell = &cref; // skip cell body if the cell is not to be written - if (skip_cell_body (cref)) { + if (cref.is_real_ghost_cell ()) { continue; } diff --git a/src/plugins/streamers/oasis/db_plugin/gsiDeclDbOASIS.cc b/src/plugins/streamers/oasis/db_plugin/gsiDeclDbOASIS.cc index efa503565..90fd665de 100644 --- a/src/plugins/streamers/oasis/db_plugin/gsiDeclDbOASIS.cc +++ b/src/plugins/streamers/oasis/db_plugin/gsiDeclDbOASIS.cc @@ -38,7 +38,7 @@ static void set_oasis_read_all_properties (db::LoadLayoutOptions *options, bool options->get_options ().read_all_properties = f; } -static int get_oasis_read_all_properties (const db::LoadLayoutOptions *options) +static bool get_oasis_read_all_properties (const db::LoadLayoutOptions *options) { return options->get_options ().read_all_properties; } diff --git a/src/pya/pya/pyaCallables.cc b/src/pya/pya/pyaCallables.cc index cf5bc0eab..6cf02316b 100644 --- a/src/pya/pya/pyaCallables.cc +++ b/src/pya/pya/pyaCallables.cc @@ -1214,8 +1214,9 @@ property_setter_impl (int mid, PyObject *self, PyObject *value) // check arguments (count and type) bool is_valid = (*m)->compatible_with_num_args (1); - bool loose = (pass != 0); // loose in the second pass - if (is_valid && ! test_arg (*(*m)->begin_arguments (), value, loose, loose)) { + bool loose = (pass > 0); // loose in the second and third pass + bool object_substitution = (pass > 1); // object substitution in the third pass + if (is_valid && ! test_arg (*(*m)->begin_arguments (), value, loose, object_substitution)) { is_valid = false; } @@ -1228,7 +1229,7 @@ property_setter_impl (int mid, PyObject *self, PyObject *value) ++pass; - } while (! meth && pass < 2); + } while (! meth && pass < 3); } diff --git a/src/tl/tl/atomic/atomic.h b/src/tl/tl/atomic/atomic.h deleted file mode 100644 index 45f2af681..000000000 --- a/src/tl/tl/atomic/atomic.h +++ /dev/null @@ -1,203 +0,0 @@ -//---------------------------------------------------------------------------- -// This is free and unencumbered software released into the public domain. -// -// Anyone is free to copy, modify, publish, use, compile, sell, or distribute -// this software, either in source code form or as a compiled binary, for any -// purpose, commercial or non-commercial, and by any means. -// -// In jurisdictions that recognize copyright laws, the author or authors of -// this software dedicate any and all copyright interest in the software to the -// public domain. We make this dedication for the benefit of the public at -// large and to the detriment of our heirs and successors. We intend this -// dedication to be an overt act of relinquishment in perpetuity of all present -// and future rights to this software under copyright law. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -// For more information, please refer to -//----------------------------------------------------------------------------- - -#ifndef ATOMIC_ATOMIC_H_ -#define ATOMIC_ATOMIC_H_ - -// Macro for disallowing copying of an object. -#if __cplusplus >= 201103L -#define ATOMIC_DISALLOW_COPY(T) \ - T(const T&) = delete; \ - T& operator=(const T&) = delete; -#else -#define ATOMIC_DISALLOW_COPY(T) \ - T(const T&); \ - T& operator=(const T&); -#endif - -// A portable static assert. -#if __cplusplus >= 201103L -#define ATOMIC_STATIC_ASSERT(condition, message) \ - static_assert((condition), message) -#else -// Based on: http://stackoverflow.com/a/809465/5778708 -#define ATOMIC_STATIC_ASSERT(condition, message) \ - _impl_STATIC_ASSERT_LINE(condition, __LINE__) -#define _impl_PASTE(a, b) a##b -#ifdef __GNUC__ -#define _impl_UNUSED __attribute__((__unused__)) -#else -#define _impl_UNUSED -#endif -#define _impl_STATIC_ASSERT_LINE(condition, line) \ - typedef char _impl_PASTE( \ - STATIC_ASSERT_failed_, \ - line)[(2 * static_cast(!!(condition))) - 1] _impl_UNUSED -#endif - -#if defined(__GNUC__) || defined(__clang__) || defined(__xlc__) -#define ATOMIC_USE_GCC_INTRINSICS -#elif defined(_MSC_VER) -#define ATOMIC_USE_MSVC_INTRINSICS -#include "atomic_msvc.h" -#elif __cplusplus >= 201103L -#define ATOMIC_USE_CPP11_ATOMIC -#include -#else -#error Unsupported compiler / system. -#endif - -namespace atomic { -template -class atomic { -public: - ATOMIC_STATIC_ASSERT(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || - sizeof(T) == 8, - "Only types of size 1, 2, 4 or 8 are supported"); - - atomic() : value_(static_cast(0)) {} - - explicit atomic(const T value) : value_(value) {} - - /// @brief Performs an atomic increment operation (value + 1). - /// @returns The new value of the atomic object. - T operator++() { -#if defined(ATOMIC_USE_GCC_INTRINSICS) - return __atomic_add_fetch(&value_, 1, __ATOMIC_SEQ_CST); -#elif defined(ATOMIC_USE_MSVC_INTRINSICS) - return msvc::interlocked::increment(&value_); -#else - return ++value_; -#endif - } - - /// @brief Performs an atomic decrement operation (value - 1). - /// @returns The new value of the atomic object. - T operator--() { -#if defined(ATOMIC_USE_GCC_INTRINSICS) - return __atomic_sub_fetch(&value_, 1, __ATOMIC_SEQ_CST); -#elif defined(ATOMIC_USE_MSVC_INTRINSICS) - return msvc::interlocked::decrement(&value_); -#else - return --value_; -#endif - } - - /// @brief Performs an atomic compare-and-swap (CAS) operation. - /// - /// The value of the atomic object is only updated to the new value if the - /// old value of the atomic object matches @c expected_val. - /// - /// @param expected_val The expected value of the atomic object. - /// @param new_val The new value to write to the atomic object. - /// @returns True if new_value was written to the atomic object. - bool compare_exchange(const T expected_val, const T new_val) { -#if defined(ATOMIC_USE_GCC_INTRINSICS) - T e = expected_val; - return __atomic_compare_exchange_n( - &value_, &e, new_val, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); -#elif defined(ATOMIC_USE_MSVC_INTRINSICS) - const T old_val = - msvc::interlocked::compare_exchange(&value_, new_val, expected_val); - return (old_val == expected_val); -#else - T e = expected_val; - return value_.compare_exchange_weak(e, new_val); -#endif - } - - /// @brief Performs an atomic set operation. - /// - /// The value of the atomic object is unconditionally updated to the new - /// value. - /// - /// @param new_val The new value to write to the atomic object. - void store(const T new_val) { -#if defined(ATOMIC_USE_GCC_INTRINSICS) - __atomic_store_n(&value_, new_val, __ATOMIC_SEQ_CST); -#elif defined(ATOMIC_USE_MSVC_INTRINSICS) - (void)msvc::interlocked::exchange(&value_, new_val); -#else - value_.store(new_val); -#endif - } - - /// @returns the current value of the atomic object. - /// @note Be careful about how this is used, since any operations on the - /// returned value are inherently non-atomic. - T load() const { -#if defined(ATOMIC_USE_GCC_INTRINSICS) - return __atomic_load_n(&value_, __ATOMIC_SEQ_CST); -#elif defined(ATOMIC_USE_MSVC_INTRINSICS) - // TODO(m): Is there a better solution for MSVC? - return value_; -#else - return value_; -#endif - } - - /// @brief Performs an atomic exchange operation. - /// - /// The value of the atomic object is unconditionally updated to the new - /// value, and the old value is returned. - /// - /// @param new_val The new value to write to the atomic object. - /// @returns the old value. - T exchange(const T new_val) { -#if defined(ATOMIC_USE_GCC_INTRINSICS) - return __atomic_exchange_n(&value_, new_val, __ATOMIC_SEQ_CST); -#elif defined(ATOMIC_USE_MSVC_INTRINSICS) - return msvc::interlocked::exchange(&value_, new_val); -#else - return value_.exchange(new_val); -#endif - } - - T operator=(const T new_value) { - store(new_value); - return new_value; - } - - operator T() const { - return load(); - } - -private: -#if defined(ATOMIC_USE_GCC_INTRINSICS) || defined(ATOMIC_USE_MSVC_INTRINSICS) - volatile T value_; -#else - std::atomic value_; -#endif - - ATOMIC_DISALLOW_COPY(atomic) -}; - -} // namespace atomic - -// Undef temporary defines. -#undef ATOMIC_USE_GCC_INTRINSICS -#undef ATOMIC_USE_MSVC_INTRINSICS -#undef ATOMIC_USE_CPP11_ATOMIC - -#endif // ATOMIC_ATOMIC_H_ diff --git a/src/tl/tl/atomic/atomic_msvc.h b/src/tl/tl/atomic/atomic_msvc.h deleted file mode 100644 index d17254f48..000000000 --- a/src/tl/tl/atomic/atomic_msvc.h +++ /dev/null @@ -1,238 +0,0 @@ -//----------------------------------------------------------------------------- -// This is free and unencumbered software released into the public domain. -// -// Anyone is free to copy, modify, publish, use, compile, sell, or distribute -// this software, either in source code form or as a compiled binary, for any -// purpose, commercial or non-commercial, and by any means. -// -// In jurisdictions that recognize copyright laws, the author or authors of -// this software dedicate any and all copyright interest in the software to the -// public domain. We make this dedication for the benefit of the public at -// large and to the detriment of our heirs and successors. We intend this -// dedication to be an overt act of relinquishment in perpetuity of all present -// and future rights to this software under copyright law. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -// For more information, please refer to -//----------------------------------------------------------------------------- - -#ifndef ATOMIC_ATOMIC_MSVC_H_ -#define ATOMIC_ATOMIC_MSVC_H_ - -// Define which functions we need (don't include ). -extern "C" { -short _InterlockedIncrement16(short volatile*); -long _InterlockedIncrement(long volatile*); -__int64 _InterlockedIncrement64(__int64 volatile*); - -short _InterlockedDecrement16(short volatile*); -long _InterlockedDecrement(long volatile*); -__int64 _InterlockedDecrement64(__int64 volatile*); - -char _InterlockedExchange8(char volatile*, char); -short _InterlockedExchange16(short volatile*, short); -long __cdecl _InterlockedExchange(long volatile*, long); -__int64 _InterlockedExchange64(__int64 volatile*, __int64); - -char _InterlockedCompareExchange8(char volatile*, char, char); -short _InterlockedCompareExchange16(short volatile*, short, short); -long __cdecl _InterlockedCompareExchange(long volatile*, long, long); -__int64 _InterlockedCompareExchange64(__int64 volatile*, __int64, __int64); -}; - -// Define which functions we want to use as inline intriniscs. -#pragma intrinsic(_InterlockedIncrement) -#pragma intrinsic(_InterlockedIncrement16) - -#pragma intrinsic(_InterlockedDecrement) -#pragma intrinsic(_InterlockedDecrement16) - -#pragma intrinsic(_InterlockedCompareExchange) -#pragma intrinsic(_InterlockedCompareExchange8) -#pragma intrinsic(_InterlockedCompareExchange16) - -#pragma intrinsic(_InterlockedExchange) -#pragma intrinsic(_InterlockedExchange8) -#pragma intrinsic(_InterlockedExchange16) - -#if defined(_M_X64) -#pragma intrinsic(_InterlockedIncrement64) -#pragma intrinsic(_InterlockedDecrement64) -#pragma intrinsic(_InterlockedCompareExchange64) -#pragma intrinsic(_InterlockedExchange64) -#endif // _M_X64 - -namespace atomic { -namespace msvc { -template -struct interlocked { -}; - -template -struct interlocked { - static inline T increment(T volatile* x) { - // There's no _InterlockedIncrement8(). - char old_val, new_val; - do { - old_val = static_cast(*x); - new_val = old_val + static_cast(1); - } while (_InterlockedCompareExchange8(reinterpret_cast(x), - new_val, - old_val) != old_val); - return static_cast(new_val); - } - - static inline T decrement(T volatile* x) { - // There's no _InterlockedDecrement8(). - char old_val, new_val; - do { - old_val = static_cast(*x); - new_val = old_val - static_cast(1); - } while (_InterlockedCompareExchange8(reinterpret_cast(x), - new_val, - old_val) != old_val); - return static_cast(new_val); - } - - static inline T compare_exchange(T volatile* x, - const T new_val, - const T expected_val) { - return static_cast( - _InterlockedCompareExchange8(reinterpret_cast(x), - static_cast(new_val), - static_cast(expected_val))); - } - - static inline T exchange(T volatile* x, const T new_val) { - return static_cast(_InterlockedExchange8( - reinterpret_cast(x), static_cast(new_val))); - } -}; - -template -struct interlocked { - static inline T increment(T volatile* x) { - return static_cast( - _InterlockedIncrement16(reinterpret_cast(x))); - } - - static inline T decrement(T volatile* x) { - return static_cast( - _InterlockedDecrement16(reinterpret_cast(x))); - } - - static inline T compare_exchange(T volatile* x, - const T new_val, - const T expected_val) { - return static_cast( - _InterlockedCompareExchange16(reinterpret_cast(x), - static_cast(new_val), - static_cast(expected_val))); - } - - static inline T exchange(T volatile* x, const T new_val) { - return static_cast( - _InterlockedExchange16(reinterpret_cast(x), - static_cast(new_val))); - } -}; - -template -struct interlocked { - static inline T increment(T volatile* x) { - return static_cast( - _InterlockedIncrement(reinterpret_cast(x))); - } - - static inline T decrement(T volatile* x) { - return static_cast( - _InterlockedDecrement(reinterpret_cast(x))); - } - - static inline T compare_exchange(T volatile* x, - const T new_val, - const T expected_val) { - return static_cast( - _InterlockedCompareExchange(reinterpret_cast(x), - static_cast(new_val), - static_cast(expected_val))); - } - - static inline T exchange(T volatile* x, const T new_val) { - return static_cast(_InterlockedExchange( - reinterpret_cast(x), static_cast(new_val))); - } -}; - -template -struct interlocked { - static inline T increment(T volatile* x) { -#if defined(_M_X64) - return static_cast( - _InterlockedIncrement64(reinterpret_cast(x))); -#else - // There's no _InterlockedIncrement64() for 32-bit x86. - __int64 old_val, new_val; - do { - old_val = static_cast<__int64>(*x); - new_val = old_val + static_cast<__int64>(1); - } while (_InterlockedCompareExchange64( - reinterpret_cast(x), new_val, old_val) != - old_val); - return static_cast(new_val); -#endif // _M_X64 - } - - static inline T decrement(T volatile* x) { -#if defined(_M_X64) - return static_cast( - _InterlockedDecrement64(reinterpret_cast(x))); -#else - // There's no _InterlockedDecrement64() for 32-bit x86. - __int64 old_val, new_val; - do { - old_val = static_cast<__int64>(*x); - new_val = old_val - static_cast<__int64>(1); - } while (_InterlockedCompareExchange64( - reinterpret_cast(x), new_val, old_val) != - old_val); - return static_cast(new_val); -#endif // _M_X64 - } - - static inline T compare_exchange(T volatile* x, - const T new_val, - const T expected_val) { - return static_cast(_InterlockedCompareExchange64( - reinterpret_cast(x), - static_cast(new_val), - static_cast(expected_val))); - } - - static inline T exchange(T volatile* x, const T new_val) { -#if defined(_M_X64) - return static_cast( - _InterlockedExchange64(reinterpret_cast(x), - static_cast(new_val))); -#else - // There's no _InterlockedExchange64 for 32-bit x86. - __int64 old_val; - do { - old_val = static_cast<__int64>(*x); - } while (_InterlockedCompareExchange64( - reinterpret_cast(x), new_val, old_val) != - old_val); - return static_cast(old_val); -#endif // _M_X64 - } -}; -} // namespace msvc -} // namespace atomic - -#endif // ATOMIC_ATOMIC_MSVC_H_ diff --git a/src/tl/tl/atomic/spinlock.h b/src/tl/tl/atomic/spinlock.h deleted file mode 100644 index 9e9653b04..000000000 --- a/src/tl/tl/atomic/spinlock.h +++ /dev/null @@ -1,78 +0,0 @@ -//----------------------------------------------------------------------------- -// This is free and unencumbered software released into the public domain. -// -// Anyone is free to copy, modify, publish, use, compile, sell, or distribute -// this software, either in source code form or as a compiled binary, for any -// purpose, commercial or non-commercial, and by any means. -// -// In jurisdictions that recognize copyright laws, the author or authors of -// this software dedicate any and all copyright interest in the software to the -// public domain. We make this dedication for the benefit of the public at -// large and to the detriment of our heirs and successors. We intend this -// dedication to be an overt act of relinquishment in perpetuity of all present -// and future rights to this software under copyright law. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -// For more information, please refer to -//----------------------------------------------------------------------------- - -#ifndef ATOMIC_SPINLOCK_H_ -#define ATOMIC_SPINLOCK_H_ - -#include "atomic.h" - -namespace atomic { -class spinlock { -public: - spinlock() : value_(0) {} - - /// @brief Acquire the lock (blocking). - /// @note Trying to acquire a lock that is already held by the calling thread - /// will dead-lock (block indefinitely). - void lock() { - while (!value_.compare_exchange(UNLOCKED, LOCKED)) - ; - } - - /// @brief Release the lock. - /// @note It is an error to release a lock that has not been previously - /// acquired. - void unlock() { value_.store(UNLOCKED); } - -private: - static const int UNLOCKED = 0; - static const int LOCKED = 1; - - atomic value_; - - ATOMIC_DISALLOW_COPY(spinlock) -}; - -class lock_guard { -public: - /// @brief The constructor acquires the lock. - /// @param lock The spinlock that will be locked. - explicit lock_guard(spinlock& lock) : lock_(lock) { - lock_.lock(); - } - - /// @brief The destructor releases the lock. - ~lock_guard() { - lock_.unlock(); - } - -private: - spinlock& lock_; - - ATOMIC_DISALLOW_COPY(lock_guard) -}; - -} // namespace atomic - -#endif // ATOMIC_SPINLOCK_H_ diff --git a/src/tl/tl/tlClassRegistry.h b/src/tl/tl/tlClassRegistry.h index 6e5fd79a6..ec44b651d 100644 --- a/src/tl/tl/tlClassRegistry.h +++ b/src/tl/tl/tlClassRegistry.h @@ -217,7 +217,7 @@ class Registrar /** * @brief Constructor */ - Registrar () + Registrar () : mp_first (0) { // .. nothing yet .. diff --git a/src/tl/tl/tlEventsVar.h b/src/tl/tl/tlEventsVar.h index bdac1ff62..5b0bbaca9 100644 --- a/src/tl/tl/tlEventsVar.h +++ b/src/tl/tl/tlEventsVar.h @@ -175,13 +175,13 @@ class TL_PUBLIC_TEMPLATE event<_TMPLARGLISTP> typedef typename receivers::iterator receivers_iterator; #endif - event<_TMPLARGLISTP> () + event () : mp_destroyed_sentinel (0) { // .. nothing yet .. } - ~event<_TMPLARGLISTP> () + ~event () { if (mp_destroyed_sentinel) { *mp_destroyed_sentinel = true; diff --git a/src/tl/tl/tlThreadedWorkers.cc b/src/tl/tl/tlThreadedWorkers.cc index 089b57873..13c26230a 100644 --- a/src/tl/tl/tlThreadedWorkers.cc +++ b/src/tl/tl/tlThreadedWorkers.cc @@ -412,9 +412,9 @@ JobBase::stop () m_stopping = false; m_running = false; - m_lock.unlock (); - stopped (); + + m_lock.unlock (); } void diff --git a/src/tl/tl/tlThreads.cc b/src/tl/tl/tlThreads.cc index 671c28ddd..19ff4fa77 100644 --- a/src/tl/tl/tlThreads.cc +++ b/src/tl/tl/tlThreads.cc @@ -20,15 +20,17 @@ */ -#if !defined(HAVE_QT) || defined(HAVE_PTHREADS) - #include "tlThreads.h" #include "tlUtils.h" #include "tlTimer.h" +#include "tlSleep.h" #include "tlLog.h" #include "tlInternational.h" #include + +#if defined(HAVE_PTHREADS) + #define _TIMESPEC_DEFINED // avoids errors with pthread-win and MSVC2017 #include #include @@ -39,12 +41,71 @@ # include #endif +#endif + namespace tl { // ------------------------------------------------------------------------------- // WaitCondition implementation +#if defined(HAVE_CPP20) || !defined(HAVE_QT) || defined(HAVE_PTHREADS) + +#if defined(HAVE_CPP20) + +class WaitConditionPrivate +{ +public: + WaitConditionPrivate () + : m_condition () + { + } + + ~WaitConditionPrivate () + { + } + + bool wait (Mutex *mutex, unsigned long time) + { + bool result = true; + m_condition.clear (std::memory_order_release); + + mutex->unlock (); + + if (time != std::numeric_limits::max ()) { + while (time > 0 && ! m_condition.test (std::memory_order_acquire)) { + tl::usleep (1000); + --time; + } + result = (time != 0); + } else { + m_condition.wait (false); + } + + mutex->lock (); + + return result; + } + + void wake_all () + { + if (! m_condition.test_and_set (std::memory_order_acquire)) { + m_condition.notify_all (); + } + } + + void wake_one () + { + if (! m_condition.test_and_set (std::memory_order_acquire)) { + m_condition.notify_one (); + } + } + +private: + std::atomic_flag m_condition; +}; + +#else class WaitConditionPrivate { @@ -139,6 +200,8 @@ class WaitConditionPrivate bool m_initialized; }; +#endif + WaitCondition::WaitCondition () { mp_data = new WaitConditionPrivate (); @@ -165,9 +228,13 @@ void WaitCondition::wakeOne () mp_data->wake_one (); } +#endif + // ------------------------------------------------------------------------------- // Thread implementation +#if !(defined(HAVE_QT) && !defined(HAVE_PTHREADS)) + class ThreadPrivateData { public: @@ -401,6 +468,6 @@ ThreadStorageHolderBase *ThreadStorageBase::holder () } } -} - #endif + +} diff --git a/src/tl/tl/tlThreads.h b/src/tl/tl/tlThreads.h index 28155f863..53fcdadc4 100644 --- a/src/tl/tl/tlThreads.h +++ b/src/tl/tl/tlThreads.h @@ -33,8 +33,7 @@ # include # include #else -// atomics taken from https://github.com/mbitsnbites/atomic -# include "atomic/spinlock.h" +# include #endif namespace tl @@ -60,11 +59,21 @@ class TL_PUBLIC Mutex class TL_PUBLIC Mutex { public: - Mutex () : m_spinlock () { } - void lock () { m_spinlock.lock (); } - void unlock () { m_spinlock.unlock (); } + Mutex () { } + + void lock () + { + while (flag.test_and_set (std::memory_order_acquire)) + ; + } + + void unlock () + { + flag.clear (std::memory_order_release); + } + private: - atomic::spinlock m_spinlock; + std::atomic_flag flag = ATOMIC_FLAG_INIT; }; #endif @@ -75,7 +84,7 @@ class TL_PUBLIC Mutex * available. */ -#if defined(HAVE_QT) && !defined(HAVE_PTHREADS) +#if defined(HAVE_QT) && !defined(HAVE_PTHREADS) && !defined(HAVE_CPP20) class TL_PUBLIC WaitCondition : public QWaitCondition diff --git a/testdata/python/dbShapesTest.py b/testdata/python/dbShapesTest.py index 584a37b5b..2be2b5c49 100644 --- a/testdata/python/dbShapesTest.py +++ b/testdata/python/dbShapesTest.py @@ -61,6 +61,26 @@ def test_2(self): shapes = top.shapes(l1) self.assertEqual(sys.getrefcount(shapes), 2) + # Tests the ability to take PolygonWithProperties instead of base class + # for setter + def test_3(self): + + shapes = pya.Shapes() + shapes.insert(pya.Polygon(pya.Box(0, 0, 100, 200))) + self.assertEqual(";".join([ str(s) for s in shapes.each() ]), "polygon (0,0;0,200;100,200;100,0)") + + pwp = pya.PolygonWithProperties(pya.Polygon(pya.Box(0, 0, 110, 210))) + s0 = [ s for s in shapes.each() ][0] + s0.polygon = pwp + self.assertEqual(";".join([ str(s) for s in shapes.each() ]), "polygon (0,0;0,210;110,210;110,0)") + + # with object substitution + s0.polygon = ( pya.Box(0, 0, 120, 220) ) + self.assertEqual(";".join([ str(s) for s in shapes.each() ]), "polygon (0,0;0,220;120,220;120,0)") + + # with object substitution by constructor inference + s0.polygon = pya.Box(0, 0, 130, 230) + self.assertEqual(";".join([ str(s) for s in shapes.each() ]), "polygon (0,0;0,230;130,230;130,0)") # run unit tests if __name__ == '__main__':