Skip to content

Commit a706ded

Browse files
authored
refactor: package once_flag and LazyInitWithCallOnce (#266)
1 parent 62ed51a commit a706ded

File tree

5 files changed

+147
-98
lines changed

5 files changed

+147
-98
lines changed

src/iceberg/schema.cc

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -90,56 +90,55 @@ bool Schema::Equals(const Schema& other) const {
9090
Result<std::optional<std::reference_wrapper<const SchemaField>>> Schema::FindFieldByName(
9191
std::string_view name, bool case_sensitive) const {
9292
if (case_sensitive) {
93-
ICEBERG_RETURN_UNEXPECTED(
94-
LazyInitWithCallOnce(name_to_id_flag_, [this]() { return InitNameToIdMap(); }));
95-
auto it = name_to_id_.find(name);
96-
if (it == name_to_id_.end()) return std::nullopt;
93+
ICEBERG_ASSIGN_OR_RAISE(auto name_to_id, name_to_id_.Get(*this));
94+
auto it = name_to_id.get().find(name);
95+
if (it == name_to_id.get().end()) {
96+
return std::nullopt;
97+
};
9798
return FindFieldById(it->second);
9899
}
99-
ICEBERG_RETURN_UNEXPECTED(LazyInitWithCallOnce(
100-
lowercase_name_to_id_flag_, [this]() { return InitLowerCaseNameToIdMap(); }));
101-
auto it = lowercase_name_to_id_.find(StringUtils::ToLower(name));
102-
if (it == lowercase_name_to_id_.end()) return std::nullopt;
100+
ICEBERG_ASSIGN_OR_RAISE(auto lowercase_name_to_id, lowercase_name_to_id_.Get(*this));
101+
auto it = lowercase_name_to_id.get().find(StringUtils::ToLower(name));
102+
if (it == lowercase_name_to_id.get().end()) {
103+
return std::nullopt;
104+
}
103105
return FindFieldById(it->second);
104106
}
105107

106-
Status Schema::InitIdToFieldMap() const {
107-
if (!id_to_field_.empty()) {
108-
return {};
109-
}
110-
IdToFieldVisitor visitor(id_to_field_);
111-
ICEBERG_RETURN_UNEXPECTED(VisitTypeInline(*this, &visitor));
112-
return {};
108+
Result<std::unordered_map<int32_t, std::reference_wrapper<const SchemaField>>>
109+
Schema::InitIdToFieldMap(const Schema& self) {
110+
std::unordered_map<int32_t, std::reference_wrapper<const SchemaField>> id_to_field;
111+
IdToFieldVisitor visitor(id_to_field);
112+
ICEBERG_RETURN_UNEXPECTED(VisitTypeInline(self, &visitor));
113+
return id_to_field;
113114
}
114115

115-
Status Schema::InitNameToIdMap() const {
116-
if (!name_to_id_.empty()) {
117-
return {};
118-
}
119-
NameToIdVisitor visitor(name_to_id_, /*case_sensitive=*/true);
116+
Result<std::unordered_map<std::string, int32_t, StringHash, std::equal_to<>>>
117+
Schema::InitNameToIdMap(const Schema& self) {
118+
std::unordered_map<std::string, int32_t, StringHash, std::equal_to<>> name_to_id;
119+
NameToIdVisitor visitor(name_to_id, /*case_sensitive=*/true);
120120
ICEBERG_RETURN_UNEXPECTED(
121-
VisitTypeInline(*this, &visitor, /*path=*/"", /*short_path=*/""));
121+
VisitTypeInline(self, &visitor, /*path=*/"", /*short_path=*/""));
122122
visitor.Finish();
123-
return {};
123+
return name_to_id;
124124
}
125125

126-
Status Schema::InitLowerCaseNameToIdMap() const {
127-
if (!lowercase_name_to_id_.empty()) {
128-
return {};
129-
}
130-
NameToIdVisitor visitor(lowercase_name_to_id_, /*case_sensitive=*/false);
126+
Result<std::unordered_map<std::string, int32_t, StringHash, std::equal_to<>>>
127+
Schema::InitLowerCaseNameToIdMap(const Schema& self) {
128+
std::unordered_map<std::string, int32_t, StringHash, std::equal_to<>>
129+
lowercase_name_to_id;
130+
NameToIdVisitor visitor(lowercase_name_to_id, /*case_sensitive=*/false);
131131
ICEBERG_RETURN_UNEXPECTED(
132-
VisitTypeInline(*this, &visitor, /*path=*/"", /*short_path=*/""));
132+
VisitTypeInline(self, &visitor, /*path=*/"", /*short_path=*/""));
133133
visitor.Finish();
134-
return {};
134+
return lowercase_name_to_id;
135135
}
136136

137137
Result<std::optional<std::reference_wrapper<const SchemaField>>> Schema::FindFieldById(
138138
int32_t field_id) const {
139-
ICEBERG_RETURN_UNEXPECTED(
140-
LazyInitWithCallOnce(id_to_field_flag_, [this]() { return InitIdToFieldMap(); }));
141-
auto it = id_to_field_.find(field_id);
142-
if (it == id_to_field_.end()) {
139+
ICEBERG_ASSIGN_OR_RAISE(auto id_to_field, id_to_field_.Get(*this));
140+
auto it = id_to_field.get().find(field_id);
141+
if (it == id_to_field.get().end()) {
143142
return std::nullopt;
144143
}
145144
return it->second;

src/iceberg/schema.h

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
/// and any utility functions. See iceberg/type.h and iceberg/field.h as well.
2525

2626
#include <cstdint>
27-
#include <mutex>
2827
#include <optional>
2928
#include <string>
3029
#include <unordered_set>
@@ -34,6 +33,7 @@
3433
#include "iceberg/result.h"
3534
#include "iceberg/schema_field.h"
3635
#include "iceberg/type.h"
36+
#include "iceberg/util/lazy.h"
3737
#include "iceberg/util/string_util.h"
3838

3939
namespace iceberg {
@@ -99,24 +99,20 @@ class ICEBERG_EXPORT Schema : public StructType {
9999
/// \brief Compare two schemas for equality.
100100
bool Equals(const Schema& other) const;
101101

102-
Status InitIdToFieldMap() const;
103-
Status InitNameToIdMap() const;
104-
Status InitLowerCaseNameToIdMap() const;
102+
static Result<std::unordered_map<int32_t, std::reference_wrapper<const SchemaField>>>
103+
InitIdToFieldMap(const Schema&);
104+
static Result<std::unordered_map<std::string, int32_t, StringHash, std::equal_to<>>>
105+
InitNameToIdMap(const Schema&);
106+
static Result<std::unordered_map<std::string, int32_t, StringHash, std::equal_to<>>>
107+
InitLowerCaseNameToIdMap(const Schema&);
105108

106109
const std::optional<int32_t> schema_id_;
107110
/// Mapping from field id to field.
108-
mutable std::unordered_map<int32_t, std::reference_wrapper<const SchemaField>>
109-
id_to_field_;
111+
Lazy<InitIdToFieldMap> id_to_field_;
110112
/// Mapping from field name to field id.
111-
mutable std::unordered_map<std::string, int32_t, StringHash, std::equal_to<>>
112-
name_to_id_;
113+
Lazy<InitNameToIdMap> name_to_id_;
113114
/// Mapping from lowercased field name to field id
114-
mutable std::unordered_map<std::string, int32_t, StringHash, std::equal_to<>>
115-
lowercase_name_to_id_;
116-
117-
mutable std::once_flag id_to_field_flag_;
118-
mutable std::once_flag name_to_id_flag_;
119-
mutable std::once_flag lowercase_name_to_id_flag_;
115+
Lazy<InitLowerCaseNameToIdMap> lowercase_name_to_id_;
120116
};
121117

122118
} // namespace iceberg

src/iceberg/type.cc

Lines changed: 28 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,9 @@ std::string StructType::ToString() const {
5151
std::span<const SchemaField> StructType::fields() const { return fields_; }
5252
Result<std::optional<NestedType::SchemaFieldConstRef>> StructType::GetFieldById(
5353
int32_t field_id) const {
54-
ICEBERG_RETURN_UNEXPECTED(
55-
LazyInitWithCallOnce(field_by_id_flag_, [this]() { return InitFieldById(); }));
56-
auto it = field_by_id_.find(field_id);
57-
if (it == field_by_id_.end()) return std::nullopt;
54+
ICEBERG_ASSIGN_OR_RAISE(auto field_by_id, field_by_id_.Get(*this));
55+
auto it = field_by_id.get().find(field_id);
56+
if (it == field_by_id.get().end()) return std::nullopt;
5857
return it->second;
5958
}
6059
Result<std::optional<NestedType::SchemaFieldConstRef>> StructType::GetFieldByIndex(
@@ -67,18 +66,17 @@ Result<std::optional<NestedType::SchemaFieldConstRef>> StructType::GetFieldByInd
6766
Result<std::optional<NestedType::SchemaFieldConstRef>> StructType::GetFieldByName(
6867
std::string_view name, bool case_sensitive) const {
6968
if (case_sensitive) {
70-
ICEBERG_RETURN_UNEXPECTED(LazyInitWithCallOnce(
71-
field_by_name_flag_, [this]() { return InitFieldByName(); }));
72-
auto it = field_by_name_.find(name);
73-
if (it != field_by_name_.end()) {
69+
ICEBERG_ASSIGN_OR_RAISE(auto field_by_name, field_by_name_.Get(*this));
70+
auto it = field_by_name.get().find(name);
71+
if (it != field_by_name.get().end()) {
7472
return it->second;
7573
}
7674
return std::nullopt;
7775
}
78-
ICEBERG_RETURN_UNEXPECTED(LazyInitWithCallOnce(
79-
field_by_lowercase_name_flag_, [this]() { return InitFieldByLowerCaseName(); }));
80-
auto it = field_by_lowercase_name_.find(StringUtils::ToLower(name));
81-
if (it != field_by_lowercase_name_.end()) {
76+
ICEBERG_ASSIGN_OR_RAISE(auto field_by_lowercase_name,
77+
field_by_lowercase_name_.Get(*this));
78+
auto it = field_by_lowercase_name.get().find(StringUtils::ToLower(name));
79+
if (it != field_by_lowercase_name.get().end()) {
8280
return it->second;
8381
}
8482
return std::nullopt;
@@ -90,47 +88,44 @@ bool StructType::Equals(const Type& other) const {
9088
const auto& struct_ = static_cast<const StructType&>(other);
9189
return fields_ == struct_.fields_;
9290
}
93-
Status StructType::InitFieldById() const {
94-
if (!field_by_id_.empty()) {
95-
return {};
96-
}
97-
for (const auto& field : fields_) {
98-
auto it = field_by_id_.try_emplace(field.field_id(), field);
91+
Result<std::unordered_map<int32_t, StructType::SchemaFieldConstRef>>
92+
StructType::InitFieldById(const StructType& self) {
93+
std::unordered_map<int32_t, SchemaFieldConstRef> field_by_id;
94+
for (const auto& field : self.fields_) {
95+
auto it = field_by_id.try_emplace(field.field_id(), field);
9996
if (!it.second) {
10097
return InvalidSchema("Duplicate field id found: {} (prev name: {}, curr name: {})",
10198
field.field_id(), it.first->second.get().name(), field.name());
10299
}
103100
}
104-
return {};
101+
return field_by_id;
105102
}
106-
Status StructType::InitFieldByName() const {
107-
if (!field_by_name_.empty()) {
108-
return {};
109-
}
110-
for (const auto& field : fields_) {
111-
auto it = field_by_name_.try_emplace(field.name(), field);
103+
Result<std::unordered_map<std::string_view, StructType::SchemaFieldConstRef>>
104+
StructType::InitFieldByName(const StructType& self) {
105+
std::unordered_map<std::string_view, StructType::SchemaFieldConstRef> field_by_name;
106+
for (const auto& field : self.fields_) {
107+
auto it = field_by_name.try_emplace(field.name(), field);
112108
if (!it.second) {
113109
return InvalidSchema("Duplicate field name found: {} (prev id: {}, curr id: {})",
114110
it.first->first, it.first->second.get().field_id(),
115111
field.field_id());
116112
}
117113
}
118-
return {};
114+
return field_by_name;
119115
}
120-
Status StructType::InitFieldByLowerCaseName() const {
121-
if (!field_by_lowercase_name_.empty()) {
122-
return {};
123-
}
124-
for (const auto& field : fields_) {
116+
Result<std::unordered_map<std::string, StructType::SchemaFieldConstRef>>
117+
StructType::InitFieldByLowerCaseName(const StructType& self) {
118+
std::unordered_map<std::string, SchemaFieldConstRef> field_by_lowercase_name;
119+
for (const auto& field : self.fields_) {
125120
auto it =
126-
field_by_lowercase_name_.try_emplace(StringUtils::ToLower(field.name()), field);
121+
field_by_lowercase_name.try_emplace(StringUtils::ToLower(field.name()), field);
127122
if (!it.second) {
128123
return InvalidSchema(
129124
"Duplicate lowercase field name found: {} (prev id: {}, curr id: {})",
130125
it.first->first, it.first->second.get().field_id(), field.field_id());
131126
}
132127
}
133-
return {};
128+
return field_by_lowercase_name;
134129
}
135130

136131
ListType::ListType(SchemaField element) : element_(std::move(element)) {

src/iceberg/type.h

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
#include <array>
2727
#include <cstdint>
2828
#include <memory>
29-
#include <mutex>
3029
#include <optional>
3130
#include <span>
3231
#include <string>
@@ -37,16 +36,10 @@
3736
#include "iceberg/result.h"
3837
#include "iceberg/schema_field.h"
3938
#include "iceberg/util/formattable.h"
39+
#include "iceberg/util/lazy.h"
4040

4141
namespace iceberg {
4242

43-
template <typename Func>
44-
Status LazyInitWithCallOnce(std::once_flag& flag, Func&& func) {
45-
Status status;
46-
std::call_once(flag, [&status, &func]() { status = func(); });
47-
return status;
48-
}
49-
5043
/// \brief Interface for a data type for a field.
5144
class ICEBERG_EXPORT Type : public iceberg::util::Formattable {
5245
public:
@@ -133,18 +126,17 @@ class ICEBERG_EXPORT StructType : public NestedType {
133126
protected:
134127
bool Equals(const Type& other) const override;
135128

136-
Status InitFieldById() const;
137-
Status InitFieldByName() const;
138-
Status InitFieldByLowerCaseName() const;
129+
static Result<std::unordered_map<int32_t, SchemaFieldConstRef>> InitFieldById(
130+
const StructType&);
131+
static Result<std::unordered_map<std::string_view, SchemaFieldConstRef>>
132+
InitFieldByName(const StructType&);
133+
static Result<std::unordered_map<std::string, SchemaFieldConstRef>>
134+
InitFieldByLowerCaseName(const StructType&);
139135

140136
std::vector<SchemaField> fields_;
141-
mutable std::unordered_map<int32_t, SchemaFieldConstRef> field_by_id_;
142-
mutable std::unordered_map<std::string_view, SchemaFieldConstRef> field_by_name_;
143-
mutable std::unordered_map<std::string, SchemaFieldConstRef> field_by_lowercase_name_;
144-
145-
mutable std::once_flag field_by_id_flag_;
146-
mutable std::once_flag field_by_name_flag_;
147-
mutable std::once_flag field_by_lowercase_name_flag_;
137+
Lazy<InitFieldById> field_by_id_;
138+
Lazy<InitFieldByName> field_by_name_;
139+
Lazy<InitFieldByLowerCaseName> field_by_lowercase_name_;
148140
};
149141

150142
/// \brief A data type representing a list of values.

src/iceberg/util/lazy.h

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#pragma once
21+
22+
/// \file iceberg/util/lazy.h
23+
/// Lazy initialization utility.
24+
25+
#include <concepts>
26+
#include <functional>
27+
#include <mutex>
28+
29+
#include "iceberg/result.h"
30+
#include "iceberg/util/macros.h"
31+
32+
namespace iceberg {
33+
34+
template <auto InitFunc>
35+
class Lazy {
36+
template <typename R>
37+
struct Trait;
38+
39+
template <typename R, typename... Args>
40+
struct Trait<R (*)(Args...)> {
41+
using ReturnType = R::value_type;
42+
};
43+
44+
using T = Trait<decltype(InitFunc)>::ReturnType;
45+
46+
public:
47+
template <typename... Args>
48+
requires std::invocable<decltype(InitFunc), Args...> &&
49+
std::same_as<std::invoke_result_t<decltype(InitFunc), Args...>, Result<T>>
50+
Result<std::reference_wrapper<T>> Get(Args&&... args) const {
51+
Result<T> result;
52+
std::call_once(flag_, [&result, this, &args...]() {
53+
result = InitFunc(std::forward<Args>(args)...);
54+
if (result) {
55+
this->value_ = std::move(result.value());
56+
}
57+
});
58+
ICEBERG_RETURN_UNEXPECTED(result);
59+
return std::ref(value_);
60+
}
61+
62+
private:
63+
mutable T value_;
64+
mutable std::once_flag flag_;
65+
};
66+
67+
}; // namespace iceberg

0 commit comments

Comments
 (0)